diff --git a/app/components/post_draft/send_handler/send_handler.tsx b/app/components/post_draft/send_handler/send_handler.tsx index 97c7e7e69..7c8c4b4a8 100644 --- a/app/components/post_draft/send_handler/send_handler.tsx +++ b/app/components/post_draft/send_handler/send_handler.tsx @@ -3,15 +3,14 @@ import React, {useCallback, useEffect, useState} from 'react'; import {useIntl} from 'react-intl'; -import {Alert, DeviceEventEmitter} from 'react-native'; +import {DeviceEventEmitter} from 'react-native'; import {getChannelTimezones} from '@actions/remote/channel'; import {executeCommand, handleGotoLocation} from '@actions/remote/command'; import {createPost} from '@actions/remote/post'; import {handleReactionToLatestPost} from '@actions/remote/reactions'; import {setStatus} from '@actions/remote/user'; -import {canEndCall, endCall, getEndCallMessage} from '@calls/actions/calls'; -import ClientError from '@client/rest/error'; +import {handleCallsSlashCommand} from '@calls/actions/calls'; import {Events, Screens} from '@constants'; import {PostPriorityType} from '@constants/post'; import {NOTIFY_ALL_MEMBERS} from '@constants/post_draft'; @@ -147,54 +146,19 @@ export default function SendHandler({ DraftUtils.alertChannelWideMention(intl, notifyAllMessage, doSubmitMessage, cancel); }, [intl, isTimezoneEnabled, channelTimezoneCount, doSubmitMessage]); - const handleEndCall = useCallback(async () => { - const hasPermissions = await canEndCall(serverUrl, channelId); - - if (!hasPermissions) { - Alert.alert( - intl.formatMessage({ - id: 'mobile.calls_end_permission_title', - defaultMessage: 'Error', - }), - intl.formatMessage({ - id: 'mobile.calls_end_permission_msg', - defaultMessage: 'You don\'t have permission to end the call. Please ask the call owner to end the call.', - })); - return; - } - - const message = await getEndCallMessage(serverUrl, channelId, currentUserId, intl); - const title = intl.formatMessage({id: 'mobile.calls_end_call_title', defaultMessage: 'End call'}); - - Alert.alert( - title, - message, - [ - { - text: intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'}), - }, - { - text: title, - onPress: async () => { - try { - await endCall(serverUrl, channelId); - } catch (e) { - const err = (e as ClientError).message || 'unable to complete command, see server logs'; - Alert.alert('Error', `Error: ${err}`); - } - }, - style: 'cancel', - }, - ], - ); - }, [serverUrl, channelId, currentUserId, intl]); - const sendCommand = useCallback(async () => { - if (value.trim() === '/call end') { - await handleEndCall(); - setSendingMessage(false); - clearDraft(); - return; + if (value.trim().startsWith('/call')) { + const {handled, error} = await handleCallsSlashCommand(value.trim(), serverUrl, channelId, currentUserId, intl); + if (handled) { + setSendingMessage(false); + clearDraft(); + return; + } + if (error) { + setSendingMessage(false); + DraftUtils.alertSlashCommandFailed(intl, error); + return; + } } const status = DraftUtils.getStatusFromSlashCommand(value); @@ -226,7 +190,7 @@ export default function SendHandler({ if (data?.goto_location && !value.startsWith('/leave')) { handleGotoLocation(serverUrl, intl, data.goto_location); } - }, [userIsOutOfOffice, currentUserId, intl, value, serverUrl, channelId, rootId, handleEndCall]); + }, [userIsOutOfOffice, currentUserId, intl, value, serverUrl, channelId, rootId]); const sendMessage = useCallback(() => { const notificationsToChannel = enableConfirmNotificationsToChannel && useChannelMentions; diff --git a/app/products/calls/actions/calls.ts b/app/products/calls/actions/calls.ts index 10b083ce7..f935ff42f 100644 --- a/app/products/calls/actions/calls.ts +++ b/app/products/calls/actions/calls.ts @@ -1,11 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {Alert} from 'react-native'; import InCallManager from 'react-native-incall-manager'; +import {Navigation} from 'react-native-navigation'; import {forceLogoutIfNecessary} from '@actions/remote/session'; import {fetchUsersByIds} from '@actions/remote/user'; -import {needsRecordingWillBePostedAlert} from '@calls/alerts'; +import {leaveAndJoinWithAlert, needsRecordingWillBePostedAlert} from '@calls/alerts'; import { getCallsConfig, getCallsState, @@ -18,8 +20,10 @@ import { setCallForChannel, newCurrentCall, myselfLeftCall, + getCurrentCall, + getChannelsWithCalls, } from '@calls/state'; -import {General, Preferences} from '@constants'; +import {General, Preferences, Screens} from '@constants'; import Calls from '@constants/calls'; import DatabaseManager from '@database/manager'; import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; @@ -28,6 +32,8 @@ import {getChannelById} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {getConfig, getLicense} from '@queries/servers/system'; import {getCurrentUser, getUserById} from '@queries/servers/user'; +import {dismissAllModalsAndPopToScreen} from '@screens/navigation'; +import NavigationStore from '@store/navigation_store'; import {logWarning} from '@utils/log'; import {displayUsername, getUserIdFromChannelName, isSystemAdmin} from '@utils/user'; @@ -228,7 +234,13 @@ export const enableChannelCalls = async (serverUrl: string, channelId: string, e return {}; }; -export const joinCall = async (serverUrl: string, channelId: string, userId: string, hasMicPermission: boolean): Promise<{ error?: string | Error; data?: string }> => { +export const joinCall = async ( + serverUrl: string, + channelId: string, + userId: string, + hasMicPermission: boolean, + title?: string, +): Promise<{ error?: string | Error; data?: string }> => { // Edge case: calls was disabled when app loaded, and then enabled, but app hasn't // reconnected its websocket since then (i.e., hasn't called batchLoadCalls yet) const {data: enabled} = await checkIsCallsPluginEnabled(serverUrl); @@ -246,7 +258,7 @@ export const joinCall = async (serverUrl: string, channelId: string, userId: str try { connection = await newConnection(serverUrl, channelId, () => { myselfLeftCall(); - }, setScreenShareURL, hasMicPermission); + }, setScreenShareURL, hasMicPermission, title); } catch (error: unknown) { await forceLogoutIfNecessary(serverUrl, error as ClientError); return {error: error as Error}; @@ -270,6 +282,16 @@ export const leaveCall = () => { setSpeakerphoneOn(false); }; +export const leaveCallPopCallScreen = async () => { + leaveCall(); + + // Need to pop the call screen, if it's somewhere in the stack. + if (NavigationStore.getScreensInStack().includes(Screens.CALL)) { + await dismissAllModalsAndPopToScreen(Screens.CALL, 'Call'); + Navigation.pop(Screens.CALL).catch(() => null); + } +}; + export const muteMyself = () => { if (connection) { connection.mute(); @@ -419,3 +441,89 @@ export const stopCallRecording = async (serverUrl: string, callId: string) => { return data; }; + +// handleCallsSlashCommand will return true if the slash command was handled +export const handleCallsSlashCommand = async (value: string, serverUrl: string, channelId: string, currentUserId: string, intl: IntlShape): + Promise<{ handled?: boolean; error?: string }> => { + const tokens = value.split(' '); + if (tokens.length < 2 || tokens[0] !== '/call') { + return {handled: false}; + } + + switch (tokens[1]) { + case 'end': + await handleEndCall(serverUrl, channelId, currentUserId, intl); + return {handled: true}; + case 'start': { + if (getChannelsWithCalls(serverUrl)[channelId]) { + return { + error: intl.formatMessage({ + id: 'mobile.calls_start_call_exists', + defaultMessage: 'A call is already ongoing in the channel.', + }), + }; + } + const title = tokens.length > 2 ? tokens.slice(2).join(' ') : undefined; + await leaveAndJoinWithAlert(intl, serverUrl, channelId, title); + return {handled: true}; + } + case 'join': + await leaveAndJoinWithAlert(intl, serverUrl, channelId); + return {handled: true}; + case 'leave': + if (getCurrentCall()?.channelId === channelId) { + await leaveCallPopCallScreen(); + return {handled: true}; + } + return { + error: intl.formatMessage({ + id: 'mobile.calls_not_connected', + defaultMessage: 'You\'re not connected to a call in the current channel.', + }), + }; + } + + return {handled: false}; +}; + +const handleEndCall = async (serverUrl: string, channelId: string, currentUserId: string, intl: IntlShape) => { + const hasPermissions = await canEndCall(serverUrl, channelId); + + if (!hasPermissions) { + Alert.alert( + intl.formatMessage({ + id: 'mobile.calls_end_permission_title', + defaultMessage: 'Error', + }), + intl.formatMessage({ + id: 'mobile.calls_end_permission_msg', + defaultMessage: 'You don\'t have permission to end the call. Please ask the call owner to end the call.', + })); + return; + } + + const message = await getEndCallMessage(serverUrl, channelId, currentUserId, intl); + const title = intl.formatMessage({id: 'mobile.calls_end_call_title', defaultMessage: 'End call'}); + + Alert.alert( + title, + message, + [ + { + text: intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'}), + }, + { + text: title, + onPress: async () => { + try { + await endCall(serverUrl, channelId); + } catch (e) { + const err = (e as ClientError).message || 'unable to complete command, see server logs'; + Alert.alert('Error', `Error: ${err}`); + } + }, + style: 'cancel', + }, + ], + ); +}; diff --git a/app/products/calls/actions/index.ts b/app/products/calls/actions/index.ts index e8f579c9f..612d9bfa2 100644 --- a/app/products/calls/actions/index.ts +++ b/app/products/calls/actions/index.ts @@ -12,6 +12,7 @@ export { raiseHand, unraiseHand, setSpeakerphoneOn, + handleCallsSlashCommand, } from './calls'; export {hasMicrophonePermission} from './permissions'; diff --git a/app/products/calls/alerts.ts b/app/products/calls/alerts.ts index fc954f101..8d5308d9e 100644 --- a/app/products/calls/alerts.ts +++ b/app/products/calls/alerts.ts @@ -2,17 +2,22 @@ // See LICENSE.txt for license information. import {Alert} from 'react-native'; -import {Navigation} from 'react-native-navigation'; -import {hasMicrophonePermission, joinCall, leaveCall, unmuteMyself} from '@calls/actions'; +import {hasMicrophonePermission, joinCall, unmuteMyself} from '@calls/actions'; +import {leaveCallPopCallScreen} from '@calls/actions/calls'; import {LimitRestrictedInfo} from '@calls/observers'; -import {getCallsConfig, getCallsState, setMicPermissionsGranted} from '@calls/state'; +import { + getCallsConfig, + getCallsState, + getChannelsWithCalls, + getCurrentCall, + setMicPermissionsGranted, +} from '@calls/state'; import {errorAlert} from '@calls/utils'; -import {Screens} from '@constants'; import DatabaseManager from '@database/manager'; +import {getChannelById} from '@queries/servers/channel'; import {getCurrentUser} from '@queries/servers/user'; -import {dismissAllModals, dismissAllModalsAndPopToScreen} from '@screens/navigation'; -import NavigationStore from '@store/navigation_store'; +import {isDMorGM} from '@utils/channel'; import {logError} from '@utils/log'; import {isSystemAdmin} from '@utils/user'; @@ -56,17 +61,39 @@ export const showLimitRestrictedAlert = (info: LimitRestrictedInfo, intl: IntlSh ); }; -export const leaveAndJoinWithAlert = ( +export const leaveAndJoinWithAlert = async ( intl: IntlShape, - serverUrl: string, - channelId: string, - leaveChannelName: string, - joinChannelName: string, - confirmToJoin: boolean, - newCall: boolean, - isDMorGM: boolean, + joinServerUrl: string, + joinChannelId: string, + title?: string, ) => { - if (confirmToJoin) { + let leaveChannelName = ''; + let joinChannelName = ''; + let joinChannelIsDMorGM = false; + let leaveServerUrl = ''; + let leaveChannelId = ''; + const newCall = !getChannelsWithCalls(joinServerUrl)[joinChannelId]; + + try { + const {database: joinDatabase} = DatabaseManager.getServerDatabaseAndOperator(joinServerUrl); + const joinChannel = await getChannelById(joinDatabase, joinChannelId); + joinChannelName = joinChannel?.displayName || ''; + joinChannelIsDMorGM = joinChannel ? isDMorGM(joinChannel) : false; + + const currentCall = getCurrentCall(); + if (currentCall) { + const {database: leaveDatabase} = DatabaseManager.getServerDatabaseAndOperator(currentCall.serverUrl); + const leaveChannel = await getChannelById(leaveDatabase, currentCall.channelId); + leaveChannelName = leaveChannel?.displayName || ''; + leaveServerUrl = currentCall.serverUrl; + leaveChannelId = currentCall.channelId; + } + } catch (error) { + logError('failed to getServerDatabase in leaveAndJoinWithAlert', error); + return; + } + + if (leaveServerUrl && leaveChannelId) { const {formatMessage} = intl; let joinMessage = formatMessage({ @@ -99,17 +126,24 @@ export const leaveAndJoinWithAlert = ( id: 'mobile.leave_and_join_confirmation', defaultMessage: 'Leave & Join', }), - onPress: () => doJoinCall(serverUrl, channelId, isDMorGM, newCall, intl), + onPress: () => doJoinCall(joinServerUrl, joinChannelId, joinChannelIsDMorGM, newCall, intl, title), style: 'cancel', }, ], ); } else { - doJoinCall(serverUrl, channelId, isDMorGM, newCall, intl); + doJoinCall(joinServerUrl, joinChannelId, joinChannelIsDMorGM, newCall, intl, title); } }; -const doJoinCall = async (serverUrl: string, channelId: string, isDMorGM: boolean, newCall: boolean, intl: IntlShape) => { +const doJoinCall = async ( + serverUrl: string, + channelId: string, + joinChannelIsDMorGM: boolean, + newCall: boolean, + intl: IntlShape, + title?: string, +) => { const {formatMessage} = intl; let user; @@ -155,14 +189,14 @@ const doJoinCall = async (serverUrl: string, channelId: string, isDMorGM: boolea const hasPermission = await hasMicrophonePermission(); setMicPermissionsGranted(hasPermission); - const res = await joinCall(serverUrl, channelId, user.id, hasPermission); + const res = await joinCall(serverUrl, channelId, user.id, hasPermission, title); if (res.error) { const seeLogs = formatMessage({id: 'mobile.calls_see_logs', defaultMessage: 'See server logs'}); errorAlert(res.error?.toString() || seeLogs, intl); return; } - if (isDMorGM) { + if (joinChannelIsDMorGM) { // FIXME (MM-46048) - HACK // There's a race condition between unmuting and receiving existing tracks from other participants. // Fixing this properly requires extensive and potentially breaking changes. @@ -222,14 +256,7 @@ export const recordingAlert = (isHost: boolean, intl: IntlShape) => { defaultMessage: 'Leave', }), onPress: async () => { - leaveCall(); - - // Need to pop the call screen, if it's somewhere in the stack. - await dismissAllModals(); - if (NavigationStore.getScreensInStack().includes(Screens.CALL)) { - await dismissAllModalsAndPopToScreen(Screens.CALL, 'Call'); - Navigation.pop(Screens.CALL).catch(() => null); - } + await leaveCallPopCallScreen(); }, style: 'destructive', }, 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 fd7eb2925..e651dcff4 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 @@ -27,11 +27,8 @@ type Props = { author?: UserModel; isMilitaryTime: boolean; teammateNameDisplay?: string; - currentCallChannelId?: string; - leaveChannelName?: string; - joinChannelName?: string; - joinChannelIsDMorGM?: boolean; limitRestrictedInfo?: LimitRestrictedInfo; + ccChannelId?: string; } const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { @@ -113,7 +110,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { export const CallsCustomMessage = ({ post, currentUser, author, isMilitaryTime, teammateNameDisplay, - currentCallChannelId, leaveChannelName, joinChannelName, joinChannelIsDMorGM, limitRestrictedInfo, + ccChannelId, limitRestrictedInfo, }: Props) => { const intl = useIntl(); const theme = useTheme(); @@ -121,8 +118,7 @@ export const CallsCustomMessage = ({ const serverUrl = useServerUrl(); const timezone = getUserTimezone(currentUser); - const confirmToJoin = Boolean(currentCallChannelId && currentCallChannelId !== post.channelId); - const alreadyInTheCall = Boolean(currentCallChannelId && currentCallChannelId === post.channelId); + const alreadyInTheCall = Boolean(ccChannelId && ccChannelId === post.channelId); const isLimitRestricted = Boolean(limitRestrictedInfo?.limitRestricted); const joinHandler = () => { @@ -135,7 +131,7 @@ export const CallsCustomMessage = ({ return; } - leaveAndJoinWithAlert(intl, serverUrl, post.channelId, leaveChannelName || '', joinChannelName || '', confirmToJoin, false, Boolean(joinChannelIsDMorGM)); + leaveAndJoinWithAlert(intl, serverUrl, post.channelId); }; const title = post.props.title ? ( diff --git a/app/products/calls/components/calls_custom_message/index.ts b/app/products/calls/components/calls_custom_message/index.ts index e2fa8be68..3f4234817 100644 --- a/app/products/calls/components/calls_custom_message/index.ts +++ b/app/products/calls/components/calls_custom_message/index.ts @@ -3,19 +3,16 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {combineLatest, of as of$} from 'rxjs'; +import {of as of$} from 'rxjs'; import {distinctUntilChanged, switchMap} from 'rxjs/operators'; import {CallsCustomMessage} from '@calls/components/calls_custom_message/calls_custom_message'; import {observeIsCallLimitRestricted} from '@calls/observers'; import {observeCurrentCall} from '@calls/state'; import {Preferences} from '@constants'; -import DatabaseManager from '@database/manager'; import {getPreferenceAsBool} from '@helpers/api/preference'; -import {observeChannel} from '@queries/servers/channel'; import {queryPreferencesByCategoryAndName} from '@queries/servers/preference'; import {observeCurrentUser, observeTeammateNameDisplay, observeUser} from '@queries/servers/user'; -import {isDMorGM} from '@utils/channel'; import type {WithDatabaseArgs} from '@typings/database/database'; import type PostModel from '@typings/database/models/servers/post'; @@ -43,27 +40,8 @@ const enhanced = withObservables(['post'], ({serverUrl, post, database}: OwnProp }; } - const ccDatabase = observeCurrentCall().pipe( - switchMap((call) => of$(call?.serverUrl || '')), - distinctUntilChanged(), - switchMap((url) => of$(DatabaseManager.serverDatabases[url]?.database)), - ); - const currentCallChannelId = observeCurrentCall().pipe( - switchMap((call) => of$(call?.channelId || '')), - distinctUntilChanged(), - ); - const leaveChannelName = combineLatest([ccDatabase, currentCallChannelId]).pipe( - switchMap(([db, id]) => (db && id ? observeChannel(db, id) : of$(undefined))), - switchMap((c) => of$(c ? c.displayName : '')), - distinctUntilChanged(), - ); - const joinChannel = observeChannel(database, post.channelId); - const joinChannelName = joinChannel.pipe( - switchMap((chan) => of$(chan?.displayName || '')), - distinctUntilChanged(), - ); - const joinChannelIsDMorGM = joinChannel.pipe( - switchMap((chan) => of$(chan ? isDMorGM(chan) : false)), + const ccChannelId = observeCurrentCall().pipe( + switchMap((call) => of$(call?.channelId)), distinctUntilChanged(), ); @@ -72,11 +50,8 @@ const enhanced = withObservables(['post'], ({serverUrl, post, database}: OwnProp author, isMilitaryTime, teammateNameDisplay: observeTeammateNameDisplay(database), - currentCallChannelId, - leaveChannelName, - joinChannelName, - joinChannelIsDMorGM, limitRestrictedInfo: observeIsCallLimitRestricted(database, serverUrl, post.channelId), + ccChannelId, }; }); diff --git a/app/products/calls/components/channel_info_start/channel_info_start_button.tsx b/app/products/calls/components/channel_info_start/channel_info_start_button.tsx index 6cdeb09c8..0b699eac1 100644 --- a/app/products/calls/components/channel_info_start/channel_info_start_button.tsx +++ b/app/products/calls/components/channel_info_start/channel_info_start_button.tsx @@ -14,26 +14,18 @@ import type {LimitRestrictedInfo} from '@calls/observers'; export interface Props { serverUrl: string; - displayName: string; channelId: string; - channelIsDMorGM: boolean; isACallInCurrentChannel: boolean; - confirmToJoin: boolean; alreadyInCall: boolean; - currentCallChannelName: string; dismissChannelInfo: () => void; limitRestrictedInfo: LimitRestrictedInfo; } const ChannelInfoStartButton = ({ serverUrl, - displayName, channelId, - channelIsDMorGM, isACallInCurrentChannel, - confirmToJoin, alreadyInCall, - currentCallChannelName, dismissChannelInfo, limitRestrictedInfo, }: Props) => { @@ -46,11 +38,11 @@ const ChannelInfoStartButton = ({ } else if (isLimitRestricted) { showLimitRestrictedAlert(limitRestrictedInfo, intl); } else { - leaveAndJoinWithAlert(intl, serverUrl, channelId, currentCallChannelName, displayName, confirmToJoin, !isACallInCurrentChannel, channelIsDMorGM); + leaveAndJoinWithAlert(intl, serverUrl, channelId); } dismissChannelInfo(); - }, [isLimitRestricted, alreadyInCall, dismissChannelInfo, intl, serverUrl, channelId, currentCallChannelName, displayName, confirmToJoin, isACallInCurrentChannel]); + }, [isLimitRestricted, alreadyInCall, dismissChannelInfo, intl, serverUrl, channelId, isACallInCurrentChannel]); const [tryJoin, msgPostfix] = useTryCallsFunction(toggleJoinLeave); diff --git a/app/products/calls/components/channel_info_start/index.ts b/app/products/calls/components/channel_info_start/index.ts index cef55b487..06bf3b22c 100644 --- a/app/products/calls/components/channel_info_start/index.ts +++ b/app/products/calls/components/channel_info_start/index.ts @@ -3,15 +3,12 @@ import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; import withObservables from '@nozbe/with-observables'; -import {combineLatest, of as of$} from 'rxjs'; +import {of as of$} from 'rxjs'; import {distinctUntilChanged, switchMap} from 'rxjs/operators'; import ChannelInfoStartButton from '@calls/components/channel_info_start/channel_info_start_button'; import {observeIsCallLimitRestricted} from '@calls/observers'; import {observeChannelsWithCalls, observeCurrentCall} from '@calls/state'; -import DatabaseManager from '@database/manager'; -import {observeChannel} from '@queries/servers/channel'; -import {isDMorGM} from '@utils/channel'; import type {WithDatabaseArgs} from '@typings/database/database'; @@ -21,43 +18,21 @@ type EnhanceProps = WithDatabaseArgs & { } const enhanced = withObservables([], ({serverUrl, channelId, database}: EnhanceProps) => { - const channel = observeChannel(database, channelId); - const displayName = channel.pipe( - switchMap((c) => of$(c?.displayName || '')), - distinctUntilChanged(), - ); - const channelIsDMorGM = channel.pipe( - switchMap((chan) => of$(chan ? isDMorGM(chan) : false)), - distinctUntilChanged(), - ); const isACallInCurrentChannel = observeChannelsWithCalls(serverUrl).pipe( switchMap((calls) => of$(Boolean(calls[channelId]))), distinctUntilChanged(), ); - const ccDatabase = observeCurrentCall().pipe( - switchMap((call) => of$(call?.serverUrl || '')), - distinctUntilChanged(), - switchMap((url) => of$(DatabaseManager.serverDatabases[url]?.database)), - ); const ccChannelId = observeCurrentCall().pipe( switchMap((call) => of$(call?.channelId)), distinctUntilChanged(), ); const confirmToJoin = ccChannelId.pipe(switchMap((ccId) => of$(ccId && ccId !== channelId))); const alreadyInCall = ccChannelId.pipe(switchMap((ccId) => of$(ccId && ccId === channelId))); - const currentCallChannelName = combineLatest([ccDatabase, ccChannelId]).pipe( - switchMap(([db, id]) => (db && id ? observeChannel(db, id) : of$(undefined))), - switchMap((c) => of$(c?.displayName || '')), - distinctUntilChanged(), - ); return { - displayName, - channelIsDMorGM, isACallInCurrentChannel, confirmToJoin, alreadyInCall, - currentCallChannelName, limitRestrictedInfo: observeIsCallLimitRestricted(database, serverUrl, channelId), }; }); diff --git a/app/products/calls/components/join_call_banner/index.ts b/app/products/calls/components/join_call_banner/index.ts index efaa0c32f..5e4a2ebc1 100644 --- a/app/products/calls/components/join_call_banner/index.ts +++ b/app/products/calls/components/join_call_banner/index.ts @@ -8,11 +8,9 @@ import {distinctUntilChanged, switchMap} from 'rxjs/operators'; import JoinCallBanner from '@calls/components/join_call_banner/join_call_banner'; import {observeIsCallLimitRestricted} from '@calls/observers'; -import {observeCallsState, observeCurrentCall} from '@calls/state'; +import {observeCallsState} from '@calls/state'; import {idsAreEqual} from '@calls/utils'; -import {observeChannel} from '@queries/servers/channel'; import {queryUsersById} from '@queries/servers/user'; -import {isDMorGM} from '@utils/channel'; import type {WithDatabaseArgs} from '@typings/database/database'; @@ -26,15 +24,6 @@ const enhanced = withObservables(['serverUrl', 'channelId'], ({ channelId, database, }: OwnProps & WithDatabaseArgs) => { - const channel = observeChannel(database, channelId); - const displayName = channel.pipe( - switchMap((c) => of$(c?.displayName)), - distinctUntilChanged(), - ); - const channelIsDMorGM = channel.pipe( - switchMap((chan) => of$(chan ? isDMorGM(chan) : false)), - distinctUntilChanged(), - ); const callsState = observeCallsState(serverUrl); const participants = callsState.pipe( switchMap((state) => of$(state.calls[channelId])), @@ -43,30 +32,13 @@ const enhanced = withObservables(['serverUrl', 'channelId'], ({ distinctUntilChanged((prev, curr) => idsAreEqual(prev, curr)), // Continue only if we have a different set of participant ids switchMap((ids) => (ids.length > 0 ? queryUsersById(database, ids).observeWithColumns(['last_picture_update']) : of$([]))), ); - const currentCallChannelId = observeCurrentCall().pipe( - switchMap((call) => of$(call?.channelId || undefined)), - distinctUntilChanged(), - ); - const inACall = currentCallChannelId.pipe( - switchMap((id) => of$(Boolean(id))), - distinctUntilChanged(), - ); - const currentCallChannelName = currentCallChannelId.pipe( - switchMap((id) => observeChannel(database, id || '')), - switchMap((c) => of$(c ? c.displayName : '')), - distinctUntilChanged(), - ); const channelCallStartTime = callsState.pipe( switchMap((cs) => of$(cs.calls[channelId]?.startTime || 0)), distinctUntilChanged(), ); return { - displayName, - channelIsDMorGM, participants, - inACall, - currentCallChannelName, channelCallStartTime, limitRestrictedInfo: observeIsCallLimitRestricted(database, serverUrl, channelId), }; diff --git a/app/products/calls/components/join_call_banner/join_call_banner.tsx b/app/products/calls/components/join_call_banner/join_call_banner.tsx index 2f35fc596..77928e301 100644 --- a/app/products/calls/components/join_call_banner/join_call_banner.tsx +++ b/app/products/calls/components/join_call_banner/join_call_banner.tsx @@ -21,11 +21,7 @@ import type UserModel from '@typings/database/models/servers/user'; type Props = { channelId: string; serverUrl: string; - displayName: string; - channelIsDMorGM: boolean; - inACall: boolean; participants: UserModel[]; - currentCallChannelName: string; channelCallStartTime: number; limitRestrictedInfo: LimitRestrictedInfo; } @@ -86,11 +82,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ const JoinCallBanner = ({ channelId, serverUrl, - displayName, - channelIsDMorGM, participants, - inACall, - currentCallChannelName, channelCallStartTime, limitRestrictedInfo, }: Props) => { @@ -104,7 +96,7 @@ const JoinCallBanner = ({ showLimitRestrictedAlert(limitRestrictedInfo, intl); return; } - leaveAndJoinWithAlert(intl, serverUrl, channelId, currentCallChannelName, displayName, inACall, false, channelIsDMorGM); + leaveAndJoinWithAlert(intl, serverUrl, channelId); }; return ( diff --git a/app/products/calls/connection/connection.ts b/app/products/calls/connection/connection.ts index 36d308b3e..cb72effbf 100644 --- a/app/products/calls/connection/connection.ts +++ b/app/products/calls/connection/connection.ts @@ -32,6 +32,7 @@ export async function newConnection( closeCb: () => void, setScreenShareURL: (url: string) => void, hasMicPermission: boolean, + title?: string, ) { let peer: Peer | null = null; let stream: MediaStream; @@ -250,6 +251,7 @@ export async function newConnection( } else { ws.send('join', { channelID, + title, }); } }); diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 565aca508..dd9aa2adc 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -408,6 +408,7 @@ "mobile.calls_not_available_msg": "Please contact your System Admin to enable the feature.", "mobile.calls_not_available_option": "(Not available)", "mobile.calls_not_available_title": "Calls is not enabled", + "mobile.calls_not_connected": "You're not connected to a call in the current channel.", "mobile.calls_ok": "OK", "mobile.calls_okay": "Okay", "mobile.calls_open_channel": "Open Channel", @@ -423,6 +424,7 @@ "mobile.calls_see_logs": "See server logs", "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_stop_recording": "Stop Recording", "mobile.calls_unmute": "Unmute", "mobile.calls_viewing_screen": "You are viewing {name}'s screen",