forked from Ivasoft/mattermost-mobile
Compare commits
14 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21ac2b3168 | ||
|
|
491209fdae | ||
|
|
696f8d69a3 | ||
|
|
0e06eb60fb | ||
|
|
7a672c39b5 | ||
|
|
aed2bd2d09 | ||
|
|
275992bbee | ||
|
|
82df285146 | ||
|
|
eb353936b1 | ||
|
|
996b78e027 | ||
|
|
a195a6ed53 | ||
|
|
6dc9632240 | ||
|
|
e16e89bb00 | ||
|
|
b13f07a66a |
@@ -131,8 +131,8 @@ android {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 388
|
||||
versionName "1.50.1"
|
||||
versionCode 397
|
||||
versionName "1.51.2"
|
||||
multiDexEnabled = true
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -24,7 +24,7 @@ import {TeamMembership} from '@mm-redux/types/teams';
|
||||
import {WebSocketMessage} from '@mm-redux/types/websocket';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {removeUserFromList} from '@mm-redux/utils/user_utils';
|
||||
import {loadCalls} from '@mmproducts/calls/store/actions/calls';
|
||||
import {batchLoadCalls} from '@mmproducts/calls/store/actions/calls';
|
||||
import {
|
||||
handleCallStarted,
|
||||
handleCallUserConnected,
|
||||
@@ -171,7 +171,7 @@ export function doReconnect(now: number) {
|
||||
|
||||
if (!me.error) {
|
||||
if (isSupportedServerCalls) {
|
||||
dispatch(loadCalls());
|
||||
dispatch(batchLoadCalls(true));
|
||||
}
|
||||
|
||||
const roles = [];
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import {RNFetchBlobFetchRepsonse} from 'rn-fetch-blob';
|
||||
import urlParse from 'url-parse';
|
||||
|
||||
import Calls from '@constants/calls';
|
||||
import {Options} from '@mm-redux/types/client4';
|
||||
|
||||
import * as ClientConstants from './constants';
|
||||
@@ -286,12 +287,16 @@ export default class ClientBase {
|
||||
return `${this.getUserThreadsRoute(userId, teamId)}/${threadId}`;
|
||||
}
|
||||
|
||||
getPluginsRoute() {
|
||||
return `${this.getBaseRoute()}/plugins`;
|
||||
}
|
||||
|
||||
getAppsProxyRoute() {
|
||||
return `${this.url}/plugins/com.mattermost.apps`;
|
||||
}
|
||||
|
||||
getCallsRoute() {
|
||||
return `${this.url}/plugins/com.mattermost.calls`;
|
||||
return `${this.url}/plugins/${Calls.PluginId}`;
|
||||
}
|
||||
|
||||
// Client Helpers
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import ClientPlugins, {ClientPluginsMix} from '@client/rest/plugins';
|
||||
import ClientCalls, {ClientCallsMix} from '@mmproducts/calls/client/rest';
|
||||
import mix from '@utils/mix';
|
||||
|
||||
@@ -36,7 +37,8 @@ interface Client extends ClientBase,
|
||||
ClientTeamsMix,
|
||||
ClientTosMix,
|
||||
ClientUsersMix,
|
||||
ClientCallsMix
|
||||
ClientCallsMix,
|
||||
ClientPluginsMix
|
||||
{}
|
||||
|
||||
class Client extends mix(ClientBase).with(
|
||||
@@ -55,6 +57,7 @@ class Client extends mix(ClientBase).with(
|
||||
ClientTos,
|
||||
ClientUsers,
|
||||
ClientCalls,
|
||||
ClientPlugins,
|
||||
) {}
|
||||
|
||||
const Client4 = new Client();
|
||||
|
||||
19
app/client/rest/plugins.ts
Normal file
19
app/client/rest/plugins.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ClientPluginManifest} from '@mm-redux/types/plugins';
|
||||
|
||||
export interface ClientPluginsMix {
|
||||
getPluginsManifests: () => Promise<ClientPluginManifest[]>;
|
||||
}
|
||||
|
||||
const ClientPlugins = (superclass: any) => class extends superclass {
|
||||
getPluginsManifests = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginsRoute()}/webapp`,
|
||||
{method: 'get'},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientPlugins;
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const RefreshConfigMillis = 20 * 60 * 1000; // Refresh config after 20 minutes
|
||||
|
||||
const RequiredServer = {
|
||||
FULL_VERSION: '6.3.0',
|
||||
MAJOR_VERSION: 6,
|
||||
@@ -8,4 +10,6 @@ const RequiredServer = {
|
||||
PATCH_VERSION: 0,
|
||||
};
|
||||
|
||||
export default {RequiredServer};
|
||||
const PluginId = 'com.mattermost.calls';
|
||||
|
||||
export default {RequiredServer, RefreshConfigMillis, PluginId};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Calls from '@constants/calls';
|
||||
|
||||
const WebsocketEvents = {
|
||||
POSTED: 'posted',
|
||||
POST_EDITED: 'post_edited',
|
||||
@@ -53,18 +56,18 @@ const WebsocketEvents = {
|
||||
SIDEBAR_CATEGORY_UPDATED: 'sidebar_category_updated',
|
||||
SIDEBAR_CATEGORY_DELETED: 'sidebar_category_deleted',
|
||||
SIDEBAR_CATEGORY_ORDER_UPDATED: 'sidebar_category_order_updated',
|
||||
CALLS_CHANNEL_ENABLED: 'custom_com.mattermost.calls_channel_enable_voice',
|
||||
CALLS_CHANNEL_DISABLED: 'custom_com.mattermost.calls_channel_disable_voice',
|
||||
CALLS_USER_CONNECTED: 'custom_com.mattermost.calls_user_connected',
|
||||
CALLS_USER_DISCONNECTED: 'custom_com.mattermost.calls_user_disconnected',
|
||||
CALLS_USER_MUTED: 'custom_com.mattermost.calls_user_muted',
|
||||
CALLS_USER_UNMUTED: 'custom_com.mattermost.calls_user_unmuted',
|
||||
CALLS_USER_VOICE_ON: 'custom_com.mattermost.calls_user_voice_on',
|
||||
CALLS_USER_VOICE_OFF: 'custom_com.mattermost.calls_user_voice_off',
|
||||
CALLS_CALL_START: 'custom_com.mattermost.calls_call_start',
|
||||
CALLS_SCREEN_ON: 'custom_com.mattermost.calls_user_screen_on',
|
||||
CALLS_SCREEN_OFF: 'custom_com.mattermost.calls_user_screen_off',
|
||||
CALLS_USER_RAISE_HAND: 'custom_com.mattermost.calls_user_raise_hand',
|
||||
CALLS_USER_UNRAISE_HAND: 'custom_com.mattermost.calls_user_unraise_hand',
|
||||
CALLS_CHANNEL_ENABLED: `custom_${Calls.PluginId}_channel_enable_voice`,
|
||||
CALLS_CHANNEL_DISABLED: `custom_${Calls.PluginId}_channel_disable_voice`,
|
||||
CALLS_USER_CONNECTED: `custom_${Calls.PluginId}_user_connected`,
|
||||
CALLS_USER_DISCONNECTED: `custom_${Calls.PluginId}_user_disconnected`,
|
||||
CALLS_USER_MUTED: `custom_${Calls.PluginId}_user_muted`,
|
||||
CALLS_USER_UNMUTED: `custom_${Calls.PluginId}_user_unmuted`,
|
||||
CALLS_USER_VOICE_ON: `custom_${Calls.PluginId}_user_voice_on`,
|
||||
CALLS_USER_VOICE_OFF: `custom_${Calls.PluginId}_user_voice_off`,
|
||||
CALLS_CALL_START: `custom_${Calls.PluginId}_call_start`,
|
||||
CALLS_SCREEN_ON: `custom_${Calls.PluginId}_user_screen_on`,
|
||||
CALLS_SCREEN_OFF: `custom_${Calls.PluginId}_user_screen_off`,
|
||||
CALLS_USER_RAISE_HAND: `custom_${Calls.PluginId}_user_raise_hand`,
|
||||
CALLS_USER_UNRAISE_HAND: `custom_${Calls.PluginId}_user_unraise_hand`,
|
||||
};
|
||||
export default WebsocketEvents;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ServerChannelState} from '@mmproducts/calls/store/types/calls';
|
||||
import type {ServerChannelState, ServerConfig} from '@mmproducts/calls/store/types/calls';
|
||||
|
||||
export interface ClientCallsMix {
|
||||
getEnabled: () => Promise<Boolean>;
|
||||
getCalls: () => Promise<ServerChannelState[]>;
|
||||
getCallsConfig: () => Promise<ServerConfig>;
|
||||
enableChannelCalls: (channelId: string) => Promise<ServerChannelState>;
|
||||
disableChannelCalls: (channelId: string) => Promise<ServerChannelState>;
|
||||
}
|
||||
@@ -34,7 +35,7 @@ const ClientCalls = (superclass: any) => class extends superclass {
|
||||
return this.doFetch(
|
||||
`${this.getCallsRoute()}/config`,
|
||||
{method: 'get'},
|
||||
);
|
||||
) as ServerConfig;
|
||||
};
|
||||
|
||||
enableChannelCalls = async (channelId: string) => {
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EnableDisableCalls should match snapshot if calls are disabled 1`] = `
|
||||
<React.Fragment>
|
||||
<Separator
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<channelInfoRow
|
||||
action={[Function]}
|
||||
defaultMessage="Enable Calls"
|
||||
icon="phone-outline"
|
||||
rightArrow={false}
|
||||
shouldRender={true}
|
||||
testID="test-id"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
togglable={false}
|
||||
/>
|
||||
</React.Fragment>
|
||||
`;
|
||||
|
||||
exports[`EnableDisableCalls should match snapshot if calls are enabled 1`] = `
|
||||
<React.Fragment>
|
||||
<Separator
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<channelInfoRow
|
||||
action={[Function]}
|
||||
defaultMessage="Disable Calls"
|
||||
icon="phone-outline"
|
||||
rightArrow={false}
|
||||
shouldRender={true}
|
||||
testID="test-id"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
togglable={false}
|
||||
/>
|
||||
</React.Fragment>
|
||||
`;
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import React, {useCallback} from 'react';
|
||||
import {injectIntl, IntlShape} from 'react-intl';
|
||||
import {injectIntl, intlShape, IntlShape} from 'react-intl';
|
||||
import {View, TouchableOpacity, Text} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
@@ -19,7 +19,7 @@ import type {UserProfile} from '@mm-redux/types/users';
|
||||
|
||||
type CallMessageProps = {
|
||||
actions: {
|
||||
joinCall: (channelId: string) => void;
|
||||
joinCall: (channelId: string, intl: typeof intlShape) => void;
|
||||
};
|
||||
post: Post;
|
||||
user: UserProfile;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {intlShape} from 'react-intl';
|
||||
|
||||
import {Theme} from '@mm-redux/types/theme';
|
||||
import EnableDisableCalls from '@mmproducts/calls/components/channel_info/enable_disable_calls';
|
||||
import StartCall from '@mmproducts/calls/components/channel_info/start_call';
|
||||
import {useCallsChannelSettings} from '@mmproducts/calls/hooks';
|
||||
|
||||
type Props = {
|
||||
theme: Theme;
|
||||
joinCall: (channelId: string, intl: typeof intlShape) => void;
|
||||
}
|
||||
|
||||
const CallsChannelInfo = ({theme, joinCall}: Props) => {
|
||||
const [enabled, canEnableDisable] = useCallsChannelSettings();
|
||||
|
||||
return (
|
||||
<>
|
||||
{enabled &&
|
||||
<StartCall
|
||||
testID='channel_info.start_call.action'
|
||||
theme={theme}
|
||||
joinCall={joinCall}
|
||||
/>
|
||||
}
|
||||
{canEnableDisable &&
|
||||
<EnableDisableCalls
|
||||
testID='channel_info.start_call.action'
|
||||
theme={theme}
|
||||
enabled={enabled}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CallsChannelInfo;
|
||||
@@ -2,30 +2,35 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import {getCurrentChannel} from '@mm-redux/selectors/entities/channels';
|
||||
import {Theme} from '@mm-redux/types/theme';
|
||||
import {useTryCallsFunction} from '@mmproducts/calls/hooks';
|
||||
import {disableChannelCalls, enableChannelCalls} from '@mmproducts/calls/store/actions/calls';
|
||||
import ChannelInfoRow from '@screens/channel_info/channel_info_row';
|
||||
import Separator from '@screens/channel_info/separator';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
type Props = {
|
||||
testID?: string;
|
||||
testID: string;
|
||||
theme: Theme;
|
||||
onPress: (channelId: string) => void;
|
||||
canEnableDisableCalls: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
const EnableDisableCalls = (props: Props) => {
|
||||
const {testID, canEnableDisableCalls, theme, onPress, enabled} = props;
|
||||
const EnableDisableCalls = ({testID, theme, enabled}: Props) => {
|
||||
const currentChannel = useSelector(getCurrentChannel);
|
||||
|
||||
const [tryOnPress, msgPostfix] = useTryCallsFunction(onPress);
|
||||
const handleEnableDisableCalls = useCallback(preventDoubleTap(tryOnPress), [tryOnPress]);
|
||||
const toggleCalls = useCallback(() => {
|
||||
if (enabled) {
|
||||
disableChannelCalls(currentChannel.id);
|
||||
} else {
|
||||
enableChannelCalls(currentChannel.id);
|
||||
}
|
||||
}, [enabled, currentChannel.id]);
|
||||
|
||||
if (!canEnableDisableCalls) {
|
||||
return null;
|
||||
}
|
||||
const [tryOnPress, msgPostfix] = useTryCallsFunction(toggleCalls);
|
||||
const handleEnableDisableCalls = preventDoubleTap(tryOnPress);
|
||||
|
||||
return (
|
||||
<>
|
||||
61
app/products/calls/components/channel_info/start_call.tsx
Normal file
61
app/products/calls/components/channel_info/start_call.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {injectIntl, intlShape, IntlShape} from 'react-intl';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import {getChannel, getCurrentChannel} from '@mm-redux/selectors/entities/channels';
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
import {Theme} from '@mm-redux/types/theme';
|
||||
import leaveAndJoinWithAlert from '@mmproducts/calls/components/leave_and_join_alert';
|
||||
import {useTryCallsFunction} from '@mmproducts/calls/hooks';
|
||||
import {getCalls, getCurrentCall} from '@mmproducts/calls/store/selectors/calls';
|
||||
import ChannelInfoRow from '@screens/channel_info/channel_info_row';
|
||||
import Separator from '@screens/channel_info/separator';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
type Props = {
|
||||
testID: string;
|
||||
theme: Theme;
|
||||
intl: typeof IntlShape;
|
||||
joinCall: (channelId: string, intl: typeof intlShape) => void;
|
||||
}
|
||||
|
||||
const StartCall = ({testID, theme, intl, joinCall}: Props) => {
|
||||
const currentChannel = useSelector(getCurrentChannel);
|
||||
const call = useSelector(getCalls)[currentChannel.id];
|
||||
const currentCall = useSelector(getCurrentCall);
|
||||
const currentCallChannelId = currentCall?.channelId || '';
|
||||
const callChannelName = useSelector((state: GlobalState) => getChannel(state, currentCallChannelId)?.display_name) || '';
|
||||
|
||||
const confirmToJoin = Boolean(currentCall && currentCall.channelId !== currentChannel.id);
|
||||
const alreadyInTheCall = Boolean(currentCall && call && currentCall.channelId === call.channelId);
|
||||
const ongoingCall = Boolean(call);
|
||||
|
||||
const leaveAndJoin = useCallback(() => {
|
||||
leaveAndJoinWithAlert(intl, currentChannel.id, callChannelName, currentChannel.display_name, confirmToJoin, joinCall);
|
||||
}, [intl, currentChannel.id, callChannelName, currentChannel.display_name, confirmToJoin, joinCall]);
|
||||
const [tryLeaveAndJoin, msgPostfix] = useTryCallsFunction(leaveAndJoin);
|
||||
const handleStartCall = useCallback(preventDoubleTap(tryLeaveAndJoin), [tryLeaveAndJoin]);
|
||||
|
||||
if (alreadyInTheCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separator theme={theme}/>
|
||||
<ChannelInfoRow
|
||||
testID={testID}
|
||||
action={handleStartCall}
|
||||
defaultMessage={(ongoingCall ? 'Join Ongoing Call' : 'Start Call') + msgPostfix}
|
||||
icon='phone-in-talk'
|
||||
theme={theme}
|
||||
rightArrow={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default injectIntl(StartCall);
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import Preferences from '@mm-redux/constants/preferences';
|
||||
|
||||
import EnableDisableCalls from './enable_disable_calls';
|
||||
|
||||
describe('EnableDisableCalls', () => {
|
||||
const baseProps = {
|
||||
testID: 'test-id',
|
||||
theme: Preferences.THEMES.denim,
|
||||
onPress: jest.fn(),
|
||||
canEnableDisableCalls: true,
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
test('should match snapshot if calls are disabled', () => {
|
||||
const wrapper = shallow(<EnableDisableCalls {...baseProps}/>);
|
||||
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot if calls are enabled', () => {
|
||||
const props = {...baseProps, enabled: true};
|
||||
const wrapper = shallow(<EnableDisableCalls {...props}/>);
|
||||
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should be null if you can not enable/disable calls', () => {
|
||||
const props = {...baseProps, canEnableDisableCalls: false};
|
||||
const wrapper = shallow(<EnableDisableCalls {...props}/>);
|
||||
|
||||
expect(wrapper.getElement()).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import {View, Platform} from 'react-native';
|
||||
import React, {useState, useEffect, useMemo} from 'react';
|
||||
import {View, Platform, StyleSheet} from 'react-native';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import {ViewTypes} from '@constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
const {
|
||||
IOS_TOP_PORTRAIT,
|
||||
@@ -15,46 +14,48 @@ const {
|
||||
ANDROID_TOP_PORTRAIT,
|
||||
} = ViewTypes;
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme(() => {
|
||||
const insets = useSafeAreaInsets();
|
||||
let topBarHeight = ANDROID_TOP_PORTRAIT;
|
||||
if (Platform.OS === 'ios') {
|
||||
topBarHeight = (IOS_TOP_PORTRAIT - STATUS_BAR_HEIGHT) + insets.top;
|
||||
}
|
||||
let topBarHeight = ANDROID_TOP_PORTRAIT;
|
||||
if (Platform.OS === 'ios') {
|
||||
topBarHeight = (IOS_TOP_PORTRAIT - STATUS_BAR_HEIGHT);
|
||||
}
|
||||
|
||||
return {
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
top: topBarHeight,
|
||||
width: '100%',
|
||||
...Platform.select({
|
||||
android: {
|
||||
elevation: 9,
|
||||
},
|
||||
ios: {
|
||||
zIndex: 9,
|
||||
},
|
||||
}),
|
||||
},
|
||||
withIndicatorBar: {
|
||||
top: topBarHeight + ViewTypes.INDICATOR_BAR_HEIGHT,
|
||||
},
|
||||
};
|
||||
const style = StyleSheet.create({
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
...Platform.select({
|
||||
android: {
|
||||
elevation: 9,
|
||||
},
|
||||
ios: {
|
||||
zIndex: 9,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNodeArray;
|
||||
children: React.ReactChildren;
|
||||
}
|
||||
|
||||
const FloatingCallContainer = (props: Props) => {
|
||||
const style = getStyleSheet(props);
|
||||
const insets = useSafeAreaInsets();
|
||||
const [indicatorBarVisible, setIndicatorBarVisible] = useState(false);
|
||||
useEffect(() => {
|
||||
EventEmitter.on(ViewTypes.INDICATOR_BAR_VISIBLE, setIndicatorBarVisible);
|
||||
return () => EventEmitter.off(ViewTypes.INDICATOR_BAR_VISIBLE, setIndicatorBarVisible);
|
||||
}, []);
|
||||
|
||||
const wrapperTop = useMemo(() => ({
|
||||
top: topBarHeight + insets.top,
|
||||
}), [insets]);
|
||||
|
||||
const withIndicatorBar = useMemo(() => ({
|
||||
top: wrapperTop.top + ViewTypes.INDICATOR_BAR_HEIGHT,
|
||||
}), [wrapperTop]);
|
||||
|
||||
return (
|
||||
<View style={[style.wrapper, indicatorBarVisible ? style.withIndicatorBar : undefined]}>
|
||||
<View style={[style.wrapper, wrapperTop, indicatorBarVisible ? withIndicatorBar : undefined]}>
|
||||
{props.children}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {Call} from '@mmproducts/calls/store/types/calls';
|
||||
|
||||
type Props = {
|
||||
actions: {
|
||||
joinCall: (channelId: string) => any;
|
||||
joinCall: (channelId: string, intl: typeof IntlShape) => void;
|
||||
};
|
||||
theme: Theme;
|
||||
call: Call;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import {IntlShape} from 'react-intl';
|
||||
import {Alert} from 'react-native';
|
||||
|
||||
export default function leaveAndJoinWithAlert(intl: typeof IntlShape, channelId: string, callChannelName: string, currentChannelName: string, confirmToJoin: boolean, joinCall: (channelId: string) => void) {
|
||||
export default function leaveAndJoinWithAlert(intl: typeof IntlShape, channelId: string, callChannelName: string, currentChannelName: string, confirmToJoin: boolean, joinCall: (channelId: string, intl: typeof IntlShape) => void) {
|
||||
if (confirmToJoin) {
|
||||
Alert.alert(
|
||||
'Are you sure you want to switch to a different call?',
|
||||
@@ -15,12 +15,12 @@ export default function leaveAndJoinWithAlert(intl: typeof IntlShape, channelId:
|
||||
},
|
||||
{
|
||||
text: 'Leave & Join',
|
||||
onPress: () => joinCall(channelId),
|
||||
onPress: () => joinCall(channelId, intl),
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
);
|
||||
} else {
|
||||
joinCall(channelId);
|
||||
joinCall(channelId, intl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StartCall should match snapshot 1`] = `
|
||||
<React.Fragment>
|
||||
<Separator
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<channelInfoRow
|
||||
action={[Function]}
|
||||
defaultMessage="Start Call"
|
||||
icon="phone-in-talk"
|
||||
rightArrow={false}
|
||||
shouldRender={true}
|
||||
testID="test-id"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
togglable={false}
|
||||
/>
|
||||
</React.Fragment>
|
||||
`;
|
||||
|
||||
exports[`StartCall should match snapshot when there is already an ongoing call in the channel 1`] = `
|
||||
<React.Fragment>
|
||||
<Separator
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<channelInfoRow
|
||||
action={[Function]}
|
||||
defaultMessage="Join Ongoing Call"
|
||||
icon="phone-in-talk"
|
||||
rightArrow={false}
|
||||
shouldRender={true}
|
||||
testID="test-id"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
togglable={false}
|
||||
/>
|
||||
</React.Fragment>
|
||||
`;
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators, Dispatch} from 'redux';
|
||||
|
||||
import {getChannel, getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
|
||||
import {joinCall} from '@mmproducts/calls/store/actions/calls';
|
||||
import {getCalls, getCurrentCall} from '@mmproducts/calls/store/selectors/calls';
|
||||
|
||||
import StartCall from './start_call';
|
||||
|
||||
import type {GlobalState} from '@mm-redux/types/store';
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
const call = getCalls(state)[currentChannelId];
|
||||
const currentCall = getCurrentCall(state);
|
||||
return {
|
||||
confirmToJoin: Boolean(currentCall && currentCall.channelId !== currentChannelId),
|
||||
alreadyInTheCall: Boolean(currentCall && call && currentCall.channelId === call.channelId),
|
||||
callChannelName: currentCall ? getChannel(state, currentCall.channelId)?.display_name : '',
|
||||
ongoingCall: Boolean(call),
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
joinCall,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(StartCall);
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import nock from 'nock';
|
||||
import React from 'react';
|
||||
import {Alert, TouchableHighlight} from 'react-native';
|
||||
|
||||
import {Client4} from '@client/rest';
|
||||
import Preferences from '@mm-redux/constants/preferences';
|
||||
import ChannelInfoRow from '@screens/channel_info/channel_info_row';
|
||||
import {shallowWithIntl} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import StartCall from './start_call';
|
||||
|
||||
describe('StartCall', () => {
|
||||
beforeAll(async () => {
|
||||
await TestHelper.initBasic(Client4);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await TestHelper.tearDown();
|
||||
});
|
||||
|
||||
const baseProps = {
|
||||
actions: {
|
||||
joinCall: jest.fn(),
|
||||
},
|
||||
testID: 'test-id',
|
||||
theme: Preferences.THEMES.denim,
|
||||
currentChannelId: 'channel-id',
|
||||
currentChannelName: 'Channel Name',
|
||||
canStartCall: true,
|
||||
callChannelName: 'Call channel name',
|
||||
confirmToJoin: false,
|
||||
alreadyInTheCall: false,
|
||||
ongoingCall: false,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallowWithIntl(<StartCall {...baseProps}/>).dive();
|
||||
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot when there is already an ongoing call in the channel', () => {
|
||||
const props = {...baseProps, ongoingCall: true};
|
||||
const wrapper = shallowWithIntl(<StartCall {...props}/>).dive();
|
||||
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should be null when you are already in the channel call', () => {
|
||||
const props = {...baseProps, alreadyInTheCall: true};
|
||||
const wrapper = shallowWithIntl(<StartCall {...props}/>).dive();
|
||||
|
||||
expect(wrapper.getElement()).toBeNull();
|
||||
});
|
||||
|
||||
test('should be null if you can not start a call', () => {
|
||||
const props = {...baseProps, canStartCall: false};
|
||||
const wrapper = shallowWithIntl(<StartCall {...props}/>).dive();
|
||||
|
||||
expect(wrapper.getElement()).toBeNull();
|
||||
});
|
||||
|
||||
test('should start on click when calls is enabled', async () => {
|
||||
nock(Client4.getCallsRoute()).
|
||||
get('/version').
|
||||
times(2).
|
||||
reply(200, {version: 1, build: 2});
|
||||
const joinCall = jest.fn();
|
||||
const props = {...baseProps, actions: {joinCall}};
|
||||
const wrapper = shallowWithIntl(<StartCall {...props}/>).dive();
|
||||
wrapper.find(ChannelInfoRow).dive().find(TouchableHighlight).simulate('press');
|
||||
|
||||
// This is so that the awaited call within ClientCalls in products/calls/client/rest.ts has
|
||||
// a chance to be completed by nock:
|
||||
await Client4.doFetch(
|
||||
`${Client4.getUrl()}/plugins/com.mattermost.calls/version`,
|
||||
{method: 'get'},
|
||||
);
|
||||
|
||||
expect(Alert.alert).not.toHaveBeenCalled();
|
||||
expect(props.actions.joinCall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should not start on click and should show alert when calls is not enabled', async () => {
|
||||
nock(Client4.getCallsRoute()).
|
||||
get('/version').
|
||||
times(2).
|
||||
reply(404);
|
||||
const joinCall = jest.fn();
|
||||
const props = {...baseProps, actions: {joinCall}};
|
||||
const wrapper = shallowWithIntl(<StartCall {...props}/>).dive();
|
||||
wrapper.find(ChannelInfoRow).dive().find(TouchableHighlight).simulate('press');
|
||||
|
||||
// This is so that the awaited call within ClientCalls in products/calls/client/rest.ts has
|
||||
// a chance to be completed by nock:
|
||||
try {
|
||||
await Client4.doFetch(
|
||||
`${Client4.getUrl()}/plugins/com.mattermost.calls/version`,
|
||||
{method: 'get'},
|
||||
);
|
||||
} catch (e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
expect(Alert.alert).toHaveBeenCalled();
|
||||
expect(props.actions.joinCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should ask for confirmation on click', async () => {
|
||||
nock(Client4.getCallsRoute()).
|
||||
get('/version').
|
||||
times(2).
|
||||
reply(200, {version: 1, build: 2});
|
||||
const joinCall = jest.fn();
|
||||
const props = {...baseProps, confirmToJoin: true, actions: {joinCall}};
|
||||
const wrapper = shallowWithIntl(<StartCall {...props}/>).dive();
|
||||
|
||||
wrapper.find(ChannelInfoRow).dive().find(TouchableHighlight).simulate('press');
|
||||
|
||||
// This is so that the awaited call within ClientCalls in products/calls/client/rest.ts has
|
||||
// a chance to be completed by nock:
|
||||
await Client4.doFetch(
|
||||
`${Client4.getUrl()}/plugins/com.mattermost.calls/version`,
|
||||
{method: 'get'},
|
||||
);
|
||||
|
||||
expect(Alert.alert).toHaveBeenCalled();
|
||||
expect(props.actions.joinCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {injectIntl, IntlShape} from 'react-intl';
|
||||
|
||||
import {Theme} from '@mm-redux/types/theme';
|
||||
import leaveAndJoinWithAlert from '@mmproducts/calls/components/leave_and_join_alert';
|
||||
import {useTryCallsFunction} from '@mmproducts/calls/hooks';
|
||||
import ChannelInfoRow from '@screens/channel_info/channel_info_row';
|
||||
import Separator from '@screens/channel_info/separator';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
type Props = {
|
||||
actions: {
|
||||
joinCall: (channelId: string) => any;
|
||||
};
|
||||
testID?: string;
|
||||
theme: Theme;
|
||||
currentChannelId: string;
|
||||
currentChannelName: string;
|
||||
callChannelName: string;
|
||||
confirmToJoin: boolean;
|
||||
alreadyInTheCall: boolean;
|
||||
canStartCall: boolean;
|
||||
ongoingCall: boolean;
|
||||
intl: typeof IntlShape;
|
||||
}
|
||||
|
||||
const StartCall = (props: Props) => {
|
||||
const {testID, canStartCall, theme, actions, currentChannelId, callChannelName, currentChannelName, confirmToJoin, alreadyInTheCall, ongoingCall, intl} = props;
|
||||
|
||||
const leaveAndJoin = useCallback(() => {
|
||||
leaveAndJoinWithAlert(intl, currentChannelId, callChannelName, currentChannelName, confirmToJoin, actions.joinCall);
|
||||
}, [intl, currentChannelId, callChannelName, currentChannelName, confirmToJoin, actions.joinCall]);
|
||||
const [tryLeaveAndJoin, msgPostfix] = useTryCallsFunction(leaveAndJoin);
|
||||
const handleStartCall = useCallback(preventDoubleTap(tryLeaveAndJoin), [tryLeaveAndJoin]);
|
||||
|
||||
if (!canStartCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (alreadyInTheCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separator theme={theme}/>
|
||||
<ChannelInfoRow
|
||||
testID={testID}
|
||||
action={handleStartCall}
|
||||
defaultMessage={(ongoingCall ? 'Join Ongoing Call' : 'Start Call') + msgPostfix}
|
||||
icon='phone-in-talk'
|
||||
theme={theme}
|
||||
rightArrow={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default injectIntl(StartCall);
|
||||
@@ -21,7 +21,7 @@ export let client: any = null;
|
||||
const websocketConnectTimeout = 3000;
|
||||
|
||||
export async function newClient(channelID: string, closeCb: () => void, setScreenShareURL: (url: string) => void) {
|
||||
let peer: any = null;
|
||||
let peer: Peer | null = null;
|
||||
let stream: MediaStream;
|
||||
let voiceTrackAdded = false;
|
||||
let voiceTrack: MediaStreamTrack | null = null;
|
||||
@@ -50,13 +50,15 @@ export async function newClient(channelID: string, closeCb: () => void, setScree
|
||||
streams.forEach((s) => {
|
||||
s.getTracks().forEach((track: MediaStreamTrack) => {
|
||||
track.stop();
|
||||
track.release();
|
||||
});
|
||||
});
|
||||
|
||||
if (peer) {
|
||||
peer.destroy();
|
||||
}
|
||||
InCallManager.stop();
|
||||
peer?.destroy(undefined, undefined, () => {
|
||||
// Wait until the peer connection is closed, which avoids the following racy error that can cause problems with accessing the audio system in the future:
|
||||
// AVAudioSession_iOS.mm:1243 Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.
|
||||
InCallManager.stop();
|
||||
});
|
||||
|
||||
if (closeCb) {
|
||||
closeCb();
|
||||
@@ -67,7 +69,7 @@ export async function newClient(channelID: string, closeCb: () => void, setScree
|
||||
if (!peer) {
|
||||
return;
|
||||
}
|
||||
if (voiceTrackAdded) {
|
||||
if (voiceTrackAdded && voiceTrack) {
|
||||
peer.replaceTrack(voiceTrack, null, stream);
|
||||
}
|
||||
if (voiceTrack) {
|
||||
@@ -160,7 +162,7 @@ export async function newClient(channelID: string, closeCb: () => void, setScree
|
||||
ws.on('message', ({data}) => {
|
||||
const msg = JSON.parse(data);
|
||||
if (msg.type === 'answer' || msg.type === 'offer') {
|
||||
peer.signal(data);
|
||||
peer?.signal(data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useState} from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {Alert} from 'react-native';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {Client4} from '@client/rest';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {getCurrentChannel} from '@mm-redux/selectors/entities/channels';
|
||||
import {getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
|
||||
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin} from '@mm-redux/utils/user_utils';
|
||||
import {loadConfig} from '@mmproducts/calls/store/actions/calls';
|
||||
import {
|
||||
getConfig,
|
||||
isCallsExplicitlyDisabled,
|
||||
isCallsExplicitlyEnabled,
|
||||
isCallsPluginEnabled,
|
||||
} from '@mmproducts/calls/store/selectors/calls';
|
||||
|
||||
// Check if calls is enabled. If it is, then run fn; if it isn't, show an alert and set
|
||||
// msgPostfix to ' (Not Available)'.
|
||||
@@ -33,3 +45,36 @@ export const useTryCallsFunction = (fn: (channelId: string) => void) => {
|
||||
|
||||
return [tryFn, msgPostfix];
|
||||
};
|
||||
|
||||
export const useCallsChannelSettings = () => {
|
||||
const dispatch = useDispatch();
|
||||
const config = useSelector(getConfig);
|
||||
const currentChannel = useSelector(getCurrentChannel);
|
||||
const pluginEnabled = useSelector(isCallsPluginEnabled);
|
||||
const explicitlyDisabled = useSelector(isCallsExplicitlyDisabled);
|
||||
const explicitlyEnabled = useSelector(isCallsExplicitlyEnabled);
|
||||
const roles = useSelector(getCurrentUserRoles);
|
||||
|
||||
useEffect(() => {
|
||||
if (pluginEnabled) {
|
||||
dispatch(loadConfig());
|
||||
}
|
||||
}, []);
|
||||
|
||||
const isDirectMessage = currentChannel.type === General.DM_CHANNEL;
|
||||
const isGroupMessage = currentChannel.type === General.GM_CHANNEL;
|
||||
const isAdmin = checkIsAdmin(roles);
|
||||
const isChannelAdmin = isAdmin || checkIsChannelAdmin(roles);
|
||||
|
||||
const enabled = pluginEnabled && (explicitlyEnabled || (!explicitlyDisabled && config.DefaultEnabled));
|
||||
let canEnableDisable;
|
||||
if (!pluginEnabled) {
|
||||
canEnableDisable = false;
|
||||
} else if (config.AllowEnableCalls) {
|
||||
canEnableDisable = isDirectMessage || isGroupMessage || isChannelAdmin;
|
||||
} else {
|
||||
canEnableDisable = isAdmin;
|
||||
}
|
||||
|
||||
return [enabled, canEnableDisable];
|
||||
};
|
||||
|
||||
@@ -421,7 +421,12 @@ export default class Peer extends stream.Duplex {
|
||||
this.isNegotiating = true;
|
||||
}
|
||||
|
||||
_destroy(err: Error | null, cb: (error: Error | null) => void) {
|
||||
destroy(err?: Error, cb?: (error: Error | null) => void, cbPCClose?: () => void): this {
|
||||
this._destroy(err, cb, cbPCClose);
|
||||
return this;
|
||||
}
|
||||
|
||||
_destroy(err?: Error | null, cb?: (error: Error | null) => void, cbPcClose?: () => void) {
|
||||
if (this.destroyed || this.destroying) {
|
||||
return;
|
||||
}
|
||||
@@ -468,14 +473,14 @@ export default class Peer extends stream.Duplex {
|
||||
} catch (err) {} // eslint-disable-line
|
||||
|
||||
// allow events concurrent with destruction to be handled
|
||||
this.channel.onmessage = null;
|
||||
this.channel.onopen = null;
|
||||
this.channel.onclose = null;
|
||||
this.channel.onerror = null;
|
||||
this.channel.onmessage = undefined;
|
||||
this.channel.onopen = undefined;
|
||||
this.channel.onclose = undefined;
|
||||
this.channel.onerror = undefined;
|
||||
}
|
||||
if (this.pc) {
|
||||
try {
|
||||
this.pc.close();
|
||||
this.pc.close(cbPcClose);
|
||||
} catch (err) {} // eslint-disable-line
|
||||
|
||||
// allow events concurrent with destruction to be handled
|
||||
@@ -491,7 +496,7 @@ export default class Peer extends stream.Duplex {
|
||||
this.emit('error', err);
|
||||
}
|
||||
this.emit('close');
|
||||
cb(null);
|
||||
cb?.(null);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,4 +23,6 @@ export default keyMirror({
|
||||
RECEIVED_UNRAISE_HAND: null,
|
||||
SET_SCREENSHARE_URL: null,
|
||||
SET_SPEAKERPHONE: null,
|
||||
RECEIVED_CONFIG: null,
|
||||
RECEIVED_PLUGIN_ENABLED: null,
|
||||
});
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
import {IntlProvider} from 'react-intl';
|
||||
import InCallManager from 'react-native-incall-manager';
|
||||
|
||||
import {Client4} from '@client/rest';
|
||||
import configureStore from '@test/test_store';
|
||||
import * as PermissionUtils from '@utils/permission';
|
||||
|
||||
import CallsTypes from '../action_types/calls';
|
||||
|
||||
@@ -31,6 +33,18 @@ jest.mock('@client/rest', () => ({
|
||||
enabled: true,
|
||||
},
|
||||
]),
|
||||
getCallsConfig: jest.fn(() => ({
|
||||
ICEServers: ['mattermost.com'],
|
||||
AllowEnableCalls: true,
|
||||
DefaultEnabled: true,
|
||||
last_retrieved_at: 1234,
|
||||
})),
|
||||
getPluginsManifests: jest.fn(() => (
|
||||
[
|
||||
{id: 'playbooks'},
|
||||
{id: 'com.mattermost.calls'},
|
||||
]
|
||||
)),
|
||||
enableChannelCalls: jest.fn(() => null),
|
||||
disableChannelCalls: jest.fn(() => null),
|
||||
},
|
||||
@@ -67,11 +81,15 @@ describe('Actions.Calls', () => {
|
||||
let store;
|
||||
const {newClient} = require('@mmproducts/calls/connection');
|
||||
InCallManager.setSpeakerphoneOn = jest.fn();
|
||||
const intlProvider = new IntlProvider({locale: 'en'}, {});
|
||||
const {intl} = intlProvider.getChildContext();
|
||||
jest.spyOn(PermissionUtils, 'hasMicrophonePermission').mockReturnValue(true);
|
||||
|
||||
beforeEach(async () => {
|
||||
newClient.mockClear();
|
||||
Client4.setUrl.mockClear();
|
||||
Client4.getCalls.mockClear();
|
||||
Client4.getCallsConfig.mockClear();
|
||||
Client4.enableChannelCalls.mockClear();
|
||||
Client4.disableChannelCalls.mockClear();
|
||||
store = await configureStore();
|
||||
@@ -79,7 +97,7 @@ describe('Actions.Calls', () => {
|
||||
|
||||
it('joinCall', async () => {
|
||||
await store.dispatch(addFakeCall('channel-id'));
|
||||
const response = await store.dispatch(CallsActions.joinCall('channel-id'));
|
||||
const response = await store.dispatch(CallsActions.joinCall('channel-id', intl));
|
||||
const result = store.getState().entities.calls.joined;
|
||||
assert.equal('channel-id', result);
|
||||
assert.equal(response.data, 'channel-id');
|
||||
@@ -92,7 +110,7 @@ describe('Actions.Calls', () => {
|
||||
await store.dispatch(addFakeCall('channel-id'));
|
||||
expect(CallsActions.ws).toBe(null);
|
||||
|
||||
await store.dispatch(CallsActions.joinCall('channel-id'));
|
||||
await store.dispatch(CallsActions.joinCall('channel-id', intl));
|
||||
let result = store.getState().entities.calls.joined;
|
||||
assert.equal('channel-id', result);
|
||||
|
||||
@@ -108,7 +126,7 @@ describe('Actions.Calls', () => {
|
||||
|
||||
it('muteMyself', async () => {
|
||||
await store.dispatch(addFakeCall('channel-id'));
|
||||
await store.dispatch(CallsActions.joinCall('channel-id'));
|
||||
await store.dispatch(CallsActions.joinCall('channel-id', intl));
|
||||
await store.dispatch(CallsActions.muteMyself());
|
||||
expect(CallsActions.ws.mute).toBeCalled();
|
||||
await store.dispatch(CallsActions.leaveCall());
|
||||
@@ -129,6 +147,28 @@ describe('Actions.Calls', () => {
|
||||
assert.equal(store.getState().entities.calls.enabled['channel-1'], true);
|
||||
});
|
||||
|
||||
it('loadConfig', async () => {
|
||||
await store.dispatch(CallsActions.loadConfig());
|
||||
expect(Client4.getCallsConfig).toBeCalledWith();
|
||||
assert.equal(store.getState().entities.calls.config.DefaultEnabled, true);
|
||||
assert.equal(store.getState().entities.calls.config.AllowEnableCalls, true);
|
||||
});
|
||||
|
||||
it('batchLoadConfig', async () => {
|
||||
await store.dispatch(CallsActions.batchLoadCalls());
|
||||
expect(Client4.getPluginsManifests).toBeCalledWith();
|
||||
expect(Client4.getCallsConfig).toBeCalledWith();
|
||||
expect(Client4.getCalls).toBeCalledWith();
|
||||
|
||||
// For some reason the above await is not working. This helps us:
|
||||
await store.dispatch(CallsActions.enableChannelCalls('channel-1'));
|
||||
|
||||
assert.equal(store.getState().entities.calls.config.DefaultEnabled, true);
|
||||
assert.equal(store.getState().entities.calls.config.AllowEnableCalls, true);
|
||||
assert.equal(store.getState().entities.calls.calls['channel-1'].channelId, 'channel-1');
|
||||
assert.equal(store.getState().entities.calls.enabled['channel-1'], true);
|
||||
});
|
||||
|
||||
it('enableChannelCalls', async () => {
|
||||
assert.equal(store.getState().entities.calls.enabled['channel-1'], undefined);
|
||||
await store.dispatch(CallsActions.enableChannelCalls('channel-1'));
|
||||
|
||||
@@ -1,29 +1,67 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {intlShape} from 'react-intl';
|
||||
import InCallManager from 'react-native-incall-manager';
|
||||
import {batch} from 'react-redux';
|
||||
|
||||
import {Client4} from '@client/rest';
|
||||
import Calls from '@constants/calls';
|
||||
import {logError} from '@mm-redux/actions/errors';
|
||||
import {forceLogoutIfNecessary} from '@mm-redux/actions/helpers';
|
||||
import {GenericAction, ActionFunc, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
|
||||
import {
|
||||
GenericAction,
|
||||
ActionFunc,
|
||||
DispatchFunc,
|
||||
GetStateFunc,
|
||||
ActionResult,
|
||||
} from '@mm-redux/types/actions';
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
import {newClient} from '@mmproducts/calls/connection';
|
||||
import CallsTypes from '@mmproducts/calls/store/action_types/calls';
|
||||
|
||||
import type {Call, CallParticipant} from '@mmproducts/calls/store/types/calls';
|
||||
import {getConfig} from '@mmproducts/calls/store/selectors/calls';
|
||||
import {Call, CallParticipant, DefaultServerConfig} from '@mmproducts/calls/store/types/calls';
|
||||
import {hasMicrophonePermission} from '@utils/permission';
|
||||
|
||||
export let ws: any = null;
|
||||
|
||||
export function loadConfig(force = false): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||
if (!force) {
|
||||
if ((Date.now() - getConfig(getState()).last_retrieved_at) < Calls.RefreshConfigMillis) {
|
||||
return {} as GenericAction;
|
||||
}
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await Client4.getCallsConfig();
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch, getState);
|
||||
dispatch(logError(error));
|
||||
|
||||
// Reset the config to the default (off) since it looks like Calls is not enabled.
|
||||
dispatch({
|
||||
type: CallsTypes.RECEIVED_CONFIG,
|
||||
data: {...DefaultServerConfig, last_retrieved_at: Date.now()},
|
||||
});
|
||||
}
|
||||
|
||||
data = {...data, last_retrieved_at: Date.now()};
|
||||
dispatch({type: CallsTypes.RECEIVED_CONFIG, data});
|
||||
return {data};
|
||||
};
|
||||
}
|
||||
|
||||
export function loadCalls(): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||
let resp = [];
|
||||
try {
|
||||
resp = await Client4.getCalls();
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch, getState);
|
||||
dispatch(logError(error));
|
||||
return {error};
|
||||
return {};
|
||||
}
|
||||
|
||||
const callsResults: Dictionary<Call> = {};
|
||||
@@ -55,11 +93,45 @@ export function loadCalls(): ActionFunc {
|
||||
};
|
||||
|
||||
dispatch({type: CallsTypes.RECEIVED_CALLS, data});
|
||||
|
||||
return {data};
|
||||
};
|
||||
}
|
||||
|
||||
export function batchLoadCalls(forceConfig = false): ActionFunc {
|
||||
return async (dispatch: DispatchFunc) => {
|
||||
const res = await dispatch(checkIsCallsPluginEnabled());
|
||||
if (!res.data) {
|
||||
// Calls is not enabled.
|
||||
return {};
|
||||
}
|
||||
|
||||
batch(() => {
|
||||
dispatch(loadConfig(forceConfig));
|
||||
dispatch(loadCalls());
|
||||
});
|
||||
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
export function checkIsCallsPluginEnabled(): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
let data;
|
||||
try {
|
||||
data = await Client4.getPluginsManifests();
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch, getState);
|
||||
dispatch(logError(error));
|
||||
return {} as GenericAction;
|
||||
}
|
||||
|
||||
const enabled = data.findIndex((m) => m.id === Calls.PluginId) !== -1;
|
||||
dispatch({type: CallsTypes.RECEIVED_PLUGIN_ENABLED, data: enabled});
|
||||
|
||||
return {data: enabled};
|
||||
};
|
||||
}
|
||||
|
||||
export function enableChannelCalls(channelId: string): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
try {
|
||||
@@ -92,8 +164,17 @@ export function disableChannelCalls(channelId: string): ActionFunc {
|
||||
};
|
||||
}
|
||||
|
||||
export function joinCall(channelId: string): ActionFunc {
|
||||
export function joinCall(channelId: string, intl: typeof intlShape): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
// 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)
|
||||
dispatch(checkIsCallsPluginEnabled());
|
||||
|
||||
const hasPermission = await hasMicrophonePermission(intl);
|
||||
if (!hasPermission) {
|
||||
return {error: 'no permissions to microphone, unable to start call'};
|
||||
}
|
||||
|
||||
const setScreenShareURL = (url: string) => {
|
||||
dispatch({
|
||||
type: CallsTypes.SET_SCREENSHARE_URL,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import assert from 'assert';
|
||||
|
||||
import CallsTypes from '@mmproducts/calls/store/action_types/calls';
|
||||
import {DefaultServerConfig} from '@mmproducts/calls/store/types/calls';
|
||||
|
||||
import callsReducer from './calls';
|
||||
|
||||
@@ -390,3 +391,20 @@ describe('Reducers.calls.screenShareURL', () => {
|
||||
assert.deepEqual(state.screenShareURL, 'new-url');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reducers.calls.config', () => {
|
||||
it('RECEIVED_CONFIG', async () => {
|
||||
const initialState = {config: DefaultServerConfig};
|
||||
const testAction = {
|
||||
type: CallsTypes.RECEIVED_CONFIG,
|
||||
data: {
|
||||
ICEServers: ['google.com'],
|
||||
AllowEnableCalls: true,
|
||||
DefaultEnabled: true,
|
||||
last_retrieved_at: 123,
|
||||
},
|
||||
};
|
||||
const state = callsReducer(initialState, testAction);
|
||||
assert.deepEqual(state.config, testAction.data);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import {combineReducers} from 'redux';
|
||||
import {GenericAction} from '@mm-redux/types/actions';
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
import CallsTypes from '@mmproducts/calls/store/action_types/calls';
|
||||
import {Call} from '@mmproducts/calls/store/types/calls';
|
||||
import {Call, DefaultServerConfig} from '@mmproducts/calls/store/types/calls';
|
||||
|
||||
function calls(state: Dictionary<Call> = {}, action: GenericAction) {
|
||||
switch (action.type) {
|
||||
@@ -147,6 +147,16 @@ function calls(state: Dictionary<Call> = {}, action: GenericAction) {
|
||||
}
|
||||
}
|
||||
|
||||
function config(state = DefaultServerConfig, action: GenericAction) {
|
||||
switch (action.type) {
|
||||
case CallsTypes.RECEIVED_CONFIG: {
|
||||
return action.data;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function joined(state = '', action: GenericAction) {
|
||||
switch (action.type) {
|
||||
case CallsTypes.RECEIVED_MYSELF_JOINED_CALL: {
|
||||
@@ -206,10 +216,22 @@ function speakerphoneOn(state = false, action: GenericAction) {
|
||||
}
|
||||
}
|
||||
|
||||
function pluginEnabled(state = false, action: GenericAction) {
|
||||
switch (action.type) {
|
||||
case CallsTypes.RECEIVED_PLUGIN_ENABLED: {
|
||||
return action.data;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
calls,
|
||||
enabled,
|
||||
joined,
|
||||
screenShareURL,
|
||||
speakerphoneOn,
|
||||
config,
|
||||
pluginEnabled,
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('Selectors.Calls', () => {
|
||||
});
|
||||
|
||||
it('isCallsEnabled', () => {
|
||||
assert.equal(Selectors.isCallsEnabled(testState), true);
|
||||
assert.equal(Selectors.isCallsExplicitlyEnabled(testState), true);
|
||||
let newState = {
|
||||
...testState,
|
||||
entities: {
|
||||
@@ -63,7 +63,7 @@ describe('Selectors.Calls', () => {
|
||||
channels: {currentChannelId: 'channel-2'},
|
||||
},
|
||||
};
|
||||
assert.equal(Selectors.isCallsEnabled(newState), false);
|
||||
assert.equal(Selectors.isCallsExplicitlyEnabled(newState), false);
|
||||
newState = {
|
||||
...testState,
|
||||
entities: {
|
||||
@@ -71,7 +71,7 @@ describe('Selectors.Calls', () => {
|
||||
channels: {currentChannelId: 'not-valid-channel'},
|
||||
},
|
||||
};
|
||||
assert.equal(Selectors.isCallsEnabled(newState), false);
|
||||
assert.equal(Selectors.isCallsExplicitlyEnabled(newState), false);
|
||||
});
|
||||
|
||||
it('getScreenShareURL', () => {
|
||||
|
||||
@@ -8,6 +8,10 @@ import {getServerVersion} from '@mm-redux/selectors/entities/general';
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||
|
||||
export function getConfig(state: GlobalState) {
|
||||
return state.entities.calls.config;
|
||||
}
|
||||
|
||||
export function getCalls(state: GlobalState) {
|
||||
return state.entities.calls.calls;
|
||||
}
|
||||
@@ -20,8 +24,20 @@ export function getCurrentCall(state: GlobalState) {
|
||||
return state.entities.calls.calls[currentCall];
|
||||
}
|
||||
|
||||
export function isCallsEnabled(state: GlobalState) {
|
||||
return Boolean(state.entities.calls.enabled[getCurrentChannelId(state)]);
|
||||
// isCallsExplicitlyEnabled returns true if and only if calls has been explicitly enabled in the current channel
|
||||
// Both this and isCallsExplicitlyDisabled can be false if the channel has never had a call in it.
|
||||
export function isCallsExplicitlyEnabled(state: GlobalState) {
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
const enabledDict = state.entities.calls.enabled;
|
||||
return enabledDict.hasOwnProperty(currentChannelId) && enabledDict[currentChannelId];
|
||||
}
|
||||
|
||||
// isCallsExplicitlyEnabled returns true if and only if calls has been explicitly disabled in the current channel
|
||||
// Both this and isCallsExplicitlyEnabled can be false if the channel has never had a call in it.
|
||||
export function isCallsExplicitlyDisabled(state: GlobalState) {
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
const enabledDict = state.entities.calls.enabled;
|
||||
return enabledDict.hasOwnProperty(currentChannelId) && !enabledDict[currentChannelId];
|
||||
}
|
||||
|
||||
export function getScreenShareURL(state: GlobalState) {
|
||||
@@ -45,3 +61,7 @@ export function isSupportedServer(state: GlobalState) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isCallsPluginEnabled(state: GlobalState) {
|
||||
return state.entities.calls.pluginEnabled;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ export type CallsState = {
|
||||
joined: string;
|
||||
screenShareURL: string;
|
||||
speakerphoneOn: boolean;
|
||||
config: ServerConfig;
|
||||
pluginEnabled: boolean;
|
||||
}
|
||||
|
||||
export type Call = {
|
||||
@@ -53,3 +55,17 @@ export type VoiceEventData = {
|
||||
channelId: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export type ServerConfig = {
|
||||
ICEServers: string[];
|
||||
AllowEnableCalls: boolean;
|
||||
DefaultEnabled: boolean;
|
||||
last_retrieved_at: number;
|
||||
}
|
||||
|
||||
export const DefaultServerConfig = {
|
||||
ICEServers: [],
|
||||
AllowEnableCalls: false,
|
||||
DefaultEnabled: false,
|
||||
last_retrieved_at: 0,
|
||||
} as ServerConfig;
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
// See LICENSE.txt for license information.
|
||||
import {EventEmitter} from 'events';
|
||||
|
||||
import Calls from '@constants/calls';
|
||||
import {encode} from '@msgpack/msgpack/dist';
|
||||
|
||||
export default class WebSocketClient extends EventEmitter {
|
||||
private ws: WebSocket | null;
|
||||
private seqNo = 0;
|
||||
private connID = '';
|
||||
private eventPrefix = 'custom_com.mattermost.calls';
|
||||
private eventPrefix = `custom_${Calls.PluginId}`;
|
||||
|
||||
constructor(connURL: string) {
|
||||
super();
|
||||
|
||||
@@ -64,7 +64,7 @@ export default class ChannelAndroid extends ChannelBase {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {theme, viewingGlobalThreads, isSupportedServerCalls} = this.props;
|
||||
const {theme, viewingGlobalThreads, isCallsEnabled} = this.props;
|
||||
let component;
|
||||
|
||||
if (viewingGlobalThreads) {
|
||||
@@ -106,11 +106,12 @@ export default class ChannelAndroid extends ChannelBase {
|
||||
{component}
|
||||
<NetworkIndicator/>
|
||||
<AnnouncementBanner/>
|
||||
{isSupportedServerCalls &&
|
||||
{isCallsEnabled &&
|
||||
<FloatingCallContainer>
|
||||
<JoinCall/>
|
||||
<CurrentCall/>
|
||||
</FloatingCallContainer>}
|
||||
</FloatingCallContainer>
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export default class ChannelIOS extends ChannelBase {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {currentChannelId, theme, viewingGlobalThreads, isSupportedServerCalls} = this.props;
|
||||
const {currentChannelId, theme, viewingGlobalThreads, isCallsEnabled} = this.props;
|
||||
|
||||
let component;
|
||||
let renderDraftArea = false;
|
||||
@@ -85,11 +85,12 @@ export default class ChannelIOS extends ChannelBase {
|
||||
<>
|
||||
<AnnouncementBanner/>
|
||||
<NetworkIndicator/>
|
||||
{isSupportedServerCalls &&
|
||||
{isCallsEnabled &&
|
||||
<FloatingCallContainer>
|
||||
<JoinCall/>
|
||||
<CurrentCall/>
|
||||
</FloatingCallContainer>}
|
||||
</FloatingCallContainer>
|
||||
}
|
||||
</>
|
||||
);
|
||||
const header = (
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class ChannelBase extends PureComponent {
|
||||
loadChannelsForTeam: PropTypes.func.isRequired,
|
||||
selectDefaultTeam: PropTypes.func.isRequired,
|
||||
selectInitialChannel: PropTypes.func.isRequired,
|
||||
loadCalls: PropTypes.func.isRequired,
|
||||
batchLoadCalls: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
componentId: PropTypes.string.isRequired,
|
||||
currentChannelId: PropTypes.string,
|
||||
@@ -40,6 +40,7 @@ export default class ChannelBase extends PureComponent {
|
||||
viewingGlobalThreads: PropTypes.bool,
|
||||
collapsedThreadsEnabled: PropTypes.bool.isRequired,
|
||||
isSupportedServerCalls: PropTypes.bool.isRequired,
|
||||
isCallsEnabled: PropTypes.bool.isRequired,
|
||||
selectedPost: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
channel_id: PropTypes.string.isRequired,
|
||||
@@ -106,7 +107,7 @@ export default class ChannelBase extends PureComponent {
|
||||
}
|
||||
|
||||
if (this.props.isSupportedServerCalls) {
|
||||
this.props.actions.loadCalls();
|
||||
this.props.actions.batchLoadCalls();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@ import {getCurrentTeam} from '@mm-redux/selectors/entities/teams';
|
||||
import {getCurrentUserId, getCurrentUserRoles, shouldShowTermsOfService} from '@mm-redux/selectors/entities/users';
|
||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||
import {isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
|
||||
import {loadCalls} from '@mmproducts/calls/store/actions/calls';
|
||||
import {isSupportedServer as isSupportedServerForCalls} from '@mmproducts/calls/store/selectors/calls';
|
||||
import {batchLoadCalls} from '@mmproducts/calls/store/actions/calls';
|
||||
import {
|
||||
isCallsPluginEnabled,
|
||||
isSupportedServer as isSupportedServerForCalls,
|
||||
} from '@mmproducts/calls/store/selectors/calls';
|
||||
import {getViewingGlobalThreads} from '@selectors/threads';
|
||||
|
||||
import Channel from './channel';
|
||||
@@ -44,6 +47,7 @@ function mapStateToProps(state) {
|
||||
const currentChannelId = currentTeam?.delete_at === 0 ? getCurrentChannelId(state) : '';
|
||||
const collapsedThreadsEnabled = isCollapsedThreadsEnabled(state);
|
||||
const isSupportedServerCalls = isSupportedServerForCalls(state);
|
||||
const isCallsEnabled = isCallsPluginEnabled(state);
|
||||
|
||||
return {
|
||||
currentChannelId,
|
||||
@@ -58,6 +62,7 @@ function mapStateToProps(state) {
|
||||
theme: getTheme(state),
|
||||
viewingGlobalThreads: collapsedThreadsEnabled && getViewingGlobalThreads(state),
|
||||
isSupportedServerCalls,
|
||||
isCallsEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,7 +73,7 @@ function mapDispatchToProps(dispatch) {
|
||||
loadChannelsForTeam,
|
||||
selectDefaultTeam,
|
||||
selectInitialChannel,
|
||||
loadCalls,
|
||||
batchLoadCalls,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1117,79 +1117,38 @@ exports[`channelInfo should match snapshot on calls supported and calls disabled
|
||||
}
|
||||
}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<Connect(InjectIntl(StartCall))
|
||||
canStartCall={false}
|
||||
currentChannelId="1234"
|
||||
currentChannelName="Channel Name"
|
||||
joinCall={[Function]}
|
||||
testID="channel_info.start_call.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
<CallsChannelInfo
|
||||
joinCall={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
/>
|
||||
<EnableDisableCalls
|
||||
canEnableDisableCalls={true}
|
||||
enabled={false}
|
||||
onPress={[Function]}
|
||||
testID="channel_info.start_call.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
<Connect(InjectIntl(Component))
|
||||
theme={
|
||||
Object {
|
||||
@@ -1804,79 +1763,38 @@ exports[`channelInfo should match snapshot on calls supported and calls enabled
|
||||
}
|
||||
}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<Connect(InjectIntl(StartCall))
|
||||
canStartCall={true}
|
||||
currentChannelId="1234"
|
||||
currentChannelName="Channel Name"
|
||||
joinCall={[Function]}
|
||||
testID="channel_info.start_call.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
<CallsChannelInfo
|
||||
joinCall={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
/>
|
||||
<EnableDisableCalls
|
||||
canEnableDisableCalls={true}
|
||||
enabled={true}
|
||||
onPress={[Function]}
|
||||
testID="channel_info.start_call.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
<Connect(InjectIntl(Component))
|
||||
theme={
|
||||
Object {
|
||||
@@ -2491,79 +2409,38 @@ exports[`channelInfo should match snapshot on calls supported, user is not admin
|
||||
}
|
||||
}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<Connect(InjectIntl(StartCall))
|
||||
canStartCall={false}
|
||||
currentChannelId="1234"
|
||||
currentChannelName="Channel Name"
|
||||
joinCall={[Function]}
|
||||
testID="channel_info.start_call.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
<CallsChannelInfo
|
||||
joinCall={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
/>
|
||||
<EnableDisableCalls
|
||||
canEnableDisableCalls={false}
|
||||
enabled={false}
|
||||
onPress={[Function]}
|
||||
testID="channel_info.start_call.action"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
<Connect(InjectIntl(Component))
|
||||
theme={
|
||||
Object {
|
||||
|
||||
@@ -13,8 +13,7 @@ import {SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {dismissModal} from '@actions/navigation';
|
||||
import StatusBar from '@components/status_bar';
|
||||
import EnableDisableCalls from '@mmproducts/calls/components/enable_disable_calls';
|
||||
import StartCall from '@mmproducts/calls/components/start_call';
|
||||
import CallsChannelInfo from '@mmproducts/calls/components/channel_info/calls_channel_info';
|
||||
import {alertErrorWithFallback} from '@utils/general';
|
||||
import {t} from '@utils/i18n';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
@@ -51,15 +50,12 @@ export default class ChannelInfo extends PureComponent {
|
||||
currentUserId: PropTypes.string,
|
||||
isTeammateGuest: PropTypes.bool.isRequired,
|
||||
isDirectMessage: PropTypes.bool.isRequired,
|
||||
isGroupMessage: PropTypes.bool.isRequired,
|
||||
teammateId: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
customStatus: PropTypes.object,
|
||||
isCustomStatusEnabled: PropTypes.bool.isRequired,
|
||||
isCustomStatusExpired: PropTypes.bool.isRequired,
|
||||
isCustomStatusExpirySupported: PropTypes.bool.isRequired,
|
||||
isCallsEnabled: PropTypes.bool.isRequired,
|
||||
isChannelAdmin: PropTypes.bool.isRequired,
|
||||
isSupportedServerCalls: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
@@ -93,19 +89,11 @@ export default class ChannelInfo extends PureComponent {
|
||||
dismissModal();
|
||||
};
|
||||
|
||||
startCallHandler = (channelId) => {
|
||||
this.props.actions.joinCall(channelId);
|
||||
joinCallHandler = (channelId, intl) => {
|
||||
this.props.actions.joinCall(channelId, intl);
|
||||
this.close();
|
||||
};
|
||||
|
||||
toggleCalls = () => {
|
||||
if (this.props.isCallsEnabled) {
|
||||
this.props.actions.disableChannelCalls(this.props.currentChannel.id);
|
||||
} else {
|
||||
this.props.actions.enableChannelCalls(this.props.currentChannel.id);
|
||||
}
|
||||
};
|
||||
|
||||
permalinkBadTeam = () => {
|
||||
const {intl} = this.context;
|
||||
const message = {
|
||||
@@ -117,7 +105,7 @@ export default class ChannelInfo extends PureComponent {
|
||||
};
|
||||
|
||||
actionsRows = (channelIsArchived) => {
|
||||
const {currentChannel, currentUserId, isDirectMessage, isGroupMessage, theme, isCallsEnabled, isSupportedServerCalls, isChannelAdmin} = this.props;
|
||||
const {currentChannel, currentUserId, isDirectMessage, theme, isSupportedServerCalls} = this.props;
|
||||
|
||||
if (channelIsArchived) {
|
||||
return (
|
||||
@@ -179,23 +167,11 @@ export default class ChannelInfo extends PureComponent {
|
||||
theme={theme}
|
||||
/>
|
||||
{isSupportedServerCalls &&
|
||||
<>
|
||||
<StartCall
|
||||
testID='channel_info.start_call.action'
|
||||
theme={theme}
|
||||
currentChannelId={currentChannel.id}
|
||||
currentChannelName={currentChannel.display_name}
|
||||
joinCall={this.startCallHandler}
|
||||
canStartCall={isCallsEnabled}
|
||||
/>
|
||||
<EnableDisableCalls
|
||||
testID='channel_info.start_call.action'
|
||||
theme={theme}
|
||||
onPress={this.toggleCalls}
|
||||
canEnableDisableCalls={isDirectMessage || isGroupMessage || isChannelAdmin}
|
||||
enabled={isCallsEnabled}
|
||||
/>
|
||||
</>}
|
||||
<CallsChannelInfo
|
||||
theme={theme}
|
||||
joinCall={this.joinCallHandler}
|
||||
/>
|
||||
}
|
||||
<Bindings
|
||||
theme={theme}
|
||||
/>
|
||||
|
||||
@@ -10,11 +10,11 @@ import {getCustomEmojisInText} from '@mm-redux/actions/emojis';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {getCurrentChannel, getCurrentChannelStats} from '@mm-redux/selectors/entities/channels';
|
||||
import {getTeammateNameDisplaySetting, getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentUserRoles, getCurrentUserId, getUser} from '@mm-redux/selectors/entities/users';
|
||||
import {getCurrentUserId, getUser} from '@mm-redux/selectors/entities/users';
|
||||
import {getUserIdFromChannelName} from '@mm-redux/utils/channel_utils';
|
||||
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin, displayUsername} from '@mm-redux/utils/user_utils';
|
||||
import {displayUsername} from '@mm-redux/utils/user_utils';
|
||||
import {joinCall, enableChannelCalls, disableChannelCalls} from '@mmproducts/calls/store/actions/calls';
|
||||
import {isCallsEnabled, isSupportedServer} from '@mmproducts/calls/store/selectors/calls';
|
||||
import {isSupportedServer} from '@mmproducts/calls/store/selectors/calls';
|
||||
import {makeGetCustomStatus, isCustomStatusEnabled, isCustomStatusExpired, isCustomStatusExpirySupported} from '@selectors/custom_status';
|
||||
import {isGuest} from '@utils/users';
|
||||
|
||||
@@ -38,8 +38,6 @@ function makeMapStateToProps() {
|
||||
let customStatus;
|
||||
let customStatusExpired = true;
|
||||
let customStatusExpirySupported = false;
|
||||
const roles = getCurrentUserRoles(state) || '';
|
||||
const isChannelAdmin = checkIsAdmin(roles) || checkIsChannelAdmin(roles);
|
||||
const isDirectMessage = currentChannel.type === General.DM_CHANNEL;
|
||||
|
||||
if (isDirectMessage) {
|
||||
@@ -70,16 +68,13 @@ function makeMapStateToProps() {
|
||||
currentUserId,
|
||||
isTeammateGuest,
|
||||
isDirectMessage,
|
||||
isGroupMessage,
|
||||
teammateId,
|
||||
theme: getTheme(state),
|
||||
customStatus,
|
||||
isCustomStatusEnabled: customStatusEnabled,
|
||||
isCustomStatusExpired: customStatusExpired,
|
||||
isCustomStatusExpirySupported: customStatusExpirySupported,
|
||||
isCallsEnabled: isCallsEnabled(state),
|
||||
isSupportedServerCalls,
|
||||
isChannelAdmin,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ export default class SelectServer extends PureComponent {
|
||||
screen = 'SSO';
|
||||
title = formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'});
|
||||
props = {ssoType: enabledSSOs[0]};
|
||||
} else if (hasLoginForm && numberSSOs > 0) {
|
||||
} else if ((hasLoginForm && numberSSOs > 0) || numberSSOs > 1) {
|
||||
screen = 'LoginOptions';
|
||||
title = formatMessage({id: 'mobile.routes.loginOptions', defaultMessage: 'Login Chooser'});
|
||||
} else {
|
||||
|
||||
@@ -65,6 +65,21 @@ const getStoragePermissionDeniedMessage = (intl: typeof intlShape) => {
|
||||
};
|
||||
};
|
||||
|
||||
const getMicrophonePermissionDeniedMessage = (intl: typeof intlShape) => {
|
||||
const {formatMessage} = intl;
|
||||
const applicationName = DeviceInfo.getApplicationName();
|
||||
return {
|
||||
title: formatMessage({
|
||||
id: 'mobile.microphone_permission_denied_title',
|
||||
defaultMessage: '{applicationName} would like to access your microphone',
|
||||
}, {applicationName}),
|
||||
text: formatMessage({
|
||||
id: 'mobile.microphone_permission_denied_description',
|
||||
defaultMessage: 'To participate in this call, open Settings to grant Mattermost access to your microphone.',
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export const hasCameraPermission = async (intl: typeof intlShape) => {
|
||||
const {formatMessage} = intl;
|
||||
const targetSource = Platform.OS === 'ios' ? Permissions.PERMISSIONS.IOS.CAMERA : Permissions.PERMISSIONS.ANDROID.CAMERA;
|
||||
@@ -108,6 +123,49 @@ export const hasCameraPermission = async (intl: typeof intlShape) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const hasMicrophonePermission = async (intl: typeof intlShape) => {
|
||||
const {formatMessage} = intl;
|
||||
const targetSource = Platform.OS === 'ios' ? Permissions.PERMISSIONS.IOS.MICROPHONE : Permissions.PERMISSIONS.ANDROID.RECORD_AUDIO;
|
||||
const hasPermission = await Permissions.check(targetSource);
|
||||
|
||||
switch (hasPermission) {
|
||||
case Permissions.RESULTS.DENIED:
|
||||
case Permissions.RESULTS.UNAVAILABLE: {
|
||||
const permissionRequest = await Permissions.request(targetSource);
|
||||
|
||||
return permissionRequest === Permissions.RESULTS.GRANTED;
|
||||
}
|
||||
case Permissions.RESULTS.BLOCKED: {
|
||||
const grantOption = {
|
||||
text: formatMessage({
|
||||
id: 'mobile.permission_denied_retry',
|
||||
defaultMessage: 'Settings',
|
||||
}),
|
||||
onPress: () => Permissions.openSettings(),
|
||||
};
|
||||
|
||||
const {title, text} = getMicrophonePermissionDeniedMessage(intl);
|
||||
|
||||
Alert.alert(
|
||||
title,
|
||||
text,
|
||||
[
|
||||
grantOption,
|
||||
{
|
||||
text: formatMessage({
|
||||
id: 'mobile.permission_denied_dismiss',
|
||||
defaultMessage: 'Don\'t Allow',
|
||||
}),
|
||||
},
|
||||
],
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const hasPhotoPermission = async (intl: typeof intlShape) => {
|
||||
const {formatMessage} = intl;
|
||||
const targetSource = Platform.OS === 'ios' ? Permissions.PERMISSIONS.IOS.PHOTO_LIBRARY : Permissions.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE;
|
||||
|
||||
@@ -415,6 +415,8 @@
|
||||
"mobile.message_length.message": "Your current message is too long. Current character count: {count}/{max}",
|
||||
"mobile.message_length.message_split_left": "Message exceeds the character limit",
|
||||
"mobile.message_length.title": "Message Length",
|
||||
"mobile.microphone_permission_denied_description": "To participate in this call, open Settings to grant Mattermost access to your microphone.",
|
||||
"mobile.microphone_permission_denied_title": "{applicationName} would like to access your microphone",
|
||||
"mobile.more_dms.add_more": "You can add {remaining, number} more users",
|
||||
"mobile.more_dms.cannot_add_more": "You cannot add more users",
|
||||
"mobile.more_dms.one_more": "You can add 1 more user",
|
||||
|
||||
12
detox/package-lock.json
generated
12
detox/package-lock.json
generated
@@ -6546,9 +6546,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
@@ -13383,9 +13383,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
|
||||
@@ -8,20 +8,20 @@ GEM
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.568.0)
|
||||
aws-sdk-core (3.130.0)
|
||||
aws-partitions (1.579.0)
|
||||
aws-sdk-core (3.130.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.525.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.55.0)
|
||||
aws-sdk-kms (1.56.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.113.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.4.0)
|
||||
aws-sigv4 (1.5.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
@@ -36,7 +36,7 @@ GEM
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.92.0)
|
||||
excon (0.92.2)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
@@ -66,7 +66,7 @@ GEM
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.205.0)
|
||||
fastlane (2.205.2)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@@ -111,7 +111,7 @@ GEM
|
||||
fastlane-plugin-find_replace_string (0.1.0)
|
||||
fastlane-plugin-versioning_android (0.1.0)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.16.0)
|
||||
google-apis-androidpublisher_v3 (0.19.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-core (0.4.2)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
@@ -126,15 +126,15 @@ GEM
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.7.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-apis-storage_v1 (0.11.0)
|
||||
google-apis-storage_v1 (0.12.0)
|
||||
google-apis-core (>= 0.4, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.5.0)
|
||||
faraday (>= 0.17.3, < 2.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.2.0)
|
||||
google-cloud-storage (1.36.1)
|
||||
google-cloud-storage (1.36.2)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
@@ -142,7 +142,7 @@ GEM
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.1.2)
|
||||
googleauth (1.1.3)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
|
||||
@@ -909,7 +909,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 388;
|
||||
CURRENT_PROJECT_VERSION = 397;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -951,7 +951,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 388;
|
||||
CURRENT_PROJECT_VERSION = 397;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.50.1</string>
|
||||
<string>1.51.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -37,7 +37,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>388</string>
|
||||
<string>397</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.50.1</string>
|
||||
<string>1.51.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>388</string>
|
||||
<string>397</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.50.1</string>
|
||||
<string>1.51.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>388</string>
|
||||
<string>397</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
@@ -348,7 +348,7 @@ PODS:
|
||||
- ReactNativeExceptionHandler (2.10.10):
|
||||
- React-Core
|
||||
- ReactNativeIncallManager (3.3.0):
|
||||
- React
|
||||
- React-Core
|
||||
- ReactNativeKeyboardTrackingView (5.7.0):
|
||||
- React
|
||||
- ReactNativeNavigation (7.25.1):
|
||||
@@ -781,7 +781,7 @@ SPEC CHECKSUMS:
|
||||
React-runtimeexecutor: 2450b43df7ffe8e805a0b3dcb2abd4282f1f1836
|
||||
ReactCommon: d98c6c96b567f9b3a15f9fd4cc302c1eda8e3cf2
|
||||
ReactNativeExceptionHandler: b11ff67c78802b2f62eed0e10e75cb1ef7947c60
|
||||
ReactNativeIncallManager: a840ef75640518065742a893e6c6ff98ee820039
|
||||
ReactNativeIncallManager: 642c22630caadff0a0619413aff4a9da08d63df9
|
||||
ReactNativeKeyboardTrackingView: 02137fac3b2ebd330d74fa54ead48b14750a2306
|
||||
ReactNativeNavigation: 6e747bdf88f138088a105285274170b3fc0404ed
|
||||
rn-fetch-blob: 17961aec08caae68bb8fc0e5b40f93b3acfa6932
|
||||
|
||||
201
package-lock.json
generated
201
package-lock.json
generated
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.50.1",
|
||||
"version": "1.51.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.50.1",
|
||||
"version": "1.51.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache 2.0",
|
||||
"dependencies": {
|
||||
@@ -28,7 +27,7 @@
|
||||
"array.prototype.flat": "1.2.5",
|
||||
"base-64": "1.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"commonmark": "github:mattermost/commonmark.js#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"commonmark": "github:mattermost/commonmark.js#d1003be97d15414af6c21894125623c45e3f5096",
|
||||
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"deep-equal": "2.0.5",
|
||||
"deepmerge": "4.2.2",
|
||||
@@ -58,7 +57,7 @@
|
||||
"react-native-haptic-feedback": "1.13.0",
|
||||
"react-native-hw-keyboard-event": "0.0.4",
|
||||
"react-native-image-picker": "4.7.3",
|
||||
"react-native-incall-manager": "3.3.0",
|
||||
"react-native-incall-manager": "github:cpoile/react-native-incall-manager",
|
||||
"react-native-keyboard-aware-scrollview": "2.1.0",
|
||||
"react-native-keyboard-tracking-view": "5.7.0",
|
||||
"react-native-keychain": "8.0.0",
|
||||
@@ -82,7 +81,7 @@
|
||||
"react-native-svg": "12.1.1",
|
||||
"react-native-vector-icons": "9.0.0",
|
||||
"react-native-video": "5.2.0",
|
||||
"react-native-webrtc": "github:streamer45/react-native-webrtc",
|
||||
"react-native-webrtc": "github:mattermost/react-native-webrtc",
|
||||
"react-native-webview": "11.17.1",
|
||||
"react-native-youtube": "2.0.2",
|
||||
"react-redux": "7.2.6",
|
||||
@@ -6476,9 +6475,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bplist-parser": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.0.tgz",
|
||||
"integrity": "sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz",
|
||||
"integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==",
|
||||
"dependencies": {
|
||||
"big-integer": "1.6.x"
|
||||
},
|
||||
@@ -7212,7 +7211,8 @@
|
||||
},
|
||||
"node_modules/commonmark": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "git+ssh://git@github.com/mattermost/commonmark.js.git#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"resolved": "git+ssh://git@github.com/mattermost/commonmark.js.git#d1003be97d15414af6c21894125623c45e3f5096",
|
||||
"integrity": "sha512-80VOWbVPw7LzPokYFKLVDsB9xdbfb3o4vBE0fPPronTJX84vDI37iqeud20Th6r4DZtghRkR9AJjBhYmC2jlsg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"entities": "~3.0.1",
|
||||
@@ -19400,8 +19400,8 @@
|
||||
},
|
||||
"node_modules/react-native-incall-manager": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-3.3.0.tgz",
|
||||
"integrity": "sha512-SvomgHUoKqVso/BGv02b4ndBqenlqYqW3pptZz5qHwteidKFNWI6ny+PPw5X55MdK0lyTbzBoiT3DwS06Qu0lg==",
|
||||
"resolved": "git+ssh://git@github.com/cpoile/react-native-incall-manager.git#8c55b9dac0a2ab25d651fb54b504d384f9989b36",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.40.0"
|
||||
}
|
||||
@@ -19746,7 +19746,7 @@
|
||||
},
|
||||
"node_modules/react-native-webrtc": {
|
||||
"version": "1.75.3",
|
||||
"resolved": "git+ssh://git@github.com/streamer45/react-native-webrtc.git#7fe7d434892e6b29c5a6086d30b10f482db1e592",
|
||||
"resolved": "git+ssh://git@github.com/mattermost/react-native-webrtc.git#7f765758f2f67e467ebd224c1c4d00a475e400d3",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.1.2",
|
||||
"event-target-shim": "^1.0.5",
|
||||
@@ -21375,13 +21375,13 @@
|
||||
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
|
||||
},
|
||||
"node_modules/simple-plist": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.0.tgz",
|
||||
"integrity": "sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz",
|
||||
"integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==",
|
||||
"dependencies": {
|
||||
"bplist-creator": "0.1.0",
|
||||
"bplist-parser": "0.3.0",
|
||||
"plist": "^3.0.4"
|
||||
"bplist-parser": "0.3.1",
|
||||
"plist": "^3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
@@ -25837,7 +25837,8 @@
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@mattermost/react-native-paste-input/-/react-native-paste-input-0.3.6.tgz",
|
||||
"integrity": "sha512-/XwUkWfkXDPv1/N+3sILKRoqa4sElqN/fADQzkC2KHYxVKN72297vMm8s+X1n2l+y7phNQ8ZmhjjL0ghuz/1og==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@msgpack/msgpack": {
|
||||
"version": "2.7.1",
|
||||
@@ -25889,7 +25890,8 @@
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cameraroll/-/cameraroll-4.1.2.tgz",
|
||||
"integrity": "sha512-jkdhMByMKD2CZ/5MPeBieYn8vkCfC4MOTouPpBpps3I8N6HUYJk+1JnDdktVYl2WINnqXpQptDA2YptVyifYAg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@react-native-community/cli": {
|
||||
"version": "6.4.0",
|
||||
@@ -26639,7 +26641,8 @@
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/clipboard/-/clipboard-1.5.1.tgz",
|
||||
"integrity": "sha512-AHAmrkLEH5UtPaDiRqoULERHh3oNv7Dgs0bTC0hO5Z2GdNokAMPT5w8ci8aMcRemcwbtdHjxChgtjbeA38GBdA==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@react-native-community/datetimepicker": {
|
||||
"version": "5.1.0",
|
||||
@@ -26744,7 +26747,8 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.4.1.tgz",
|
||||
"integrity": "sha512-gcLfn6P2PrFAVx3AobaOzlIEevpAEf9chTpFZz7bYfc7pz8XRv7vuKTIE4hxPKZSha6XWKKplDQ0x9Pq8xX2mg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"eslint-plugin-react-native": {
|
||||
"version": "3.11.0",
|
||||
@@ -26774,13 +26778,15 @@
|
||||
"version": "0.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/masked-view/-/masked-view-0.1.11.tgz",
|
||||
"integrity": "sha512-rQfMIGSR/1r/SyN87+VD8xHHzDYeHaJq6elOSCAD+0iLagXkSI2pfA0LmSXP21uw5i3em7GkkRjfJ8wpqWXZNw==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@react-native-community/netinfo": {
|
||||
"version": "7.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-7.1.9.tgz",
|
||||
"integrity": "sha512-xxbxFherpOjQeJm3rIx6gmZNEEBqVDIcmBII5QVSN8zf3xAwmaT/RxT74ISYDkeSlZwv4eegpkD4QA1ky52CNg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@react-native-cookies/cookies": {
|
||||
"version": "6.0.11",
|
||||
@@ -26821,7 +26827,8 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.0.tgz",
|
||||
"integrity": "sha512-C0roIxajvleskEpeYYJ1+2XTg8UqZO6o/SyzqTRuEUVsVD8CbFzKSOfj9kpfzA7jbYNzZPbzUJcm9kIigY9kHg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@react-navigation/native": {
|
||||
"version": "6.0.7",
|
||||
@@ -27758,7 +27765,8 @@
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
|
||||
"integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@webpack-cli/info": {
|
||||
"version": "1.4.1",
|
||||
@@ -27774,7 +27782,8 @@
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
|
||||
"integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
@@ -27859,14 +27868,16 @@
|
||||
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"acorn-jsx": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "7.2.0",
|
||||
@@ -28254,7 +28265,8 @@
|
||||
"version": "7.0.0-bridge.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
|
||||
"integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"babel-eslint": {
|
||||
"version": "10.1.0",
|
||||
@@ -28650,9 +28662,9 @@
|
||||
}
|
||||
},
|
||||
"bplist-parser": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.0.tgz",
|
||||
"integrity": "sha512-zgmaRvT6AN1JpPPV+S0a1/FAtoxSreYDccZGIqEMSvZl9DMe70mJ7MFzpxa1X+gHVdkToE2haRUHHMiW1OdejA==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz",
|
||||
"integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==",
|
||||
"requires": {
|
||||
"big-integer": "1.6.x"
|
||||
}
|
||||
@@ -29214,8 +29226,9 @@
|
||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
|
||||
},
|
||||
"commonmark": {
|
||||
"version": "git+ssh://git@github.com/mattermost/commonmark.js.git#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"from": "commonmark@github:mattermost/commonmark.js#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"version": "git+ssh://git@github.com/mattermost/commonmark.js.git#d1003be97d15414af6c21894125623c45e3f5096",
|
||||
"integrity": "sha512-80VOWbVPw7LzPokYFKLVDsB9xdbfb3o4vBE0fPPronTJX84vDI37iqeud20Th6r4DZtghRkR9AJjBhYmC2jlsg==",
|
||||
"from": "commonmark@github:mattermost/commonmark.js#d1003be97d15414af6c21894125623c45e3f5096",
|
||||
"requires": {
|
||||
"entities": "~3.0.1",
|
||||
"mdurl": "~1.0.1",
|
||||
@@ -30551,7 +30564,8 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz",
|
||||
"integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"eslint-plugin-import": {
|
||||
"version": "2.25.4",
|
||||
@@ -30678,7 +30692,8 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz",
|
||||
"integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"eslint-plugin-react-native-globals": {
|
||||
"version": "0.1.2",
|
||||
@@ -34284,7 +34299,8 @@
|
||||
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
|
||||
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"jest-regex-util": {
|
||||
"version": "27.4.0",
|
||||
@@ -35105,7 +35121,8 @@
|
||||
"integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -35114,7 +35131,8 @@
|
||||
"resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz",
|
||||
"integrity": "sha1-a9KZwTsMRiay2iwDk81DhdYGrLk=",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
"version": "2.5.2",
|
||||
@@ -38506,7 +38524,8 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.0.tgz",
|
||||
"integrity": "sha512-yQaiOqDmoKqks56LN9MTgY06O0qQHgV4FUrikH357DydArSZHQhl0BJFqGKIZoTqi8JizF9Dxhuk1FIZD6qCaw==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-intl": {
|
||||
"version": "2.8.0",
|
||||
@@ -38714,7 +38733,8 @@
|
||||
"version": "8.4.8",
|
||||
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.4.8.tgz",
|
||||
"integrity": "sha512-92676ZWHZHsPM/EW1ulgb2MuVfjYfMWRTWMbLcrCsipkcMaZ9Traz5mpsnCS7KZpsOksnvUinzDIjsct2XGc6Q==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-document-picker": {
|
||||
"version": "8.0.0",
|
||||
@@ -38752,19 +38772,22 @@
|
||||
"version": "2.10.10",
|
||||
"resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.10.10.tgz",
|
||||
"integrity": "sha512-otAXGoZDl1689OoUJWN/rXxVbdoZ3xcmyF1uq/CsizdLwwyZqVGd6d+p/vbYvnF996FfEyAEBnHrdFxulTn51w==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-fast-image": {
|
||||
"version": "8.5.11",
|
||||
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.5.11.tgz",
|
||||
"integrity": "sha512-cNW4bIJg3nvKaheG8vGMfqCt5LMWX9MS5+wMudgKIHbGO51spRr4sgnlhVgwHLcZ5aeNOVJ8CPRxDIWKRq/0QA==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-file-viewer": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-file-viewer/-/react-native-file-viewer-2.1.5.tgz",
|
||||
"integrity": "sha512-MGC6sx9jsqHdefhVQ6o0akdsPGpkXgiIbpygb2Sg4g4bh7v6K1cardLV1NwGB9A6u1yICOSDT/MOC//9Ez6EUg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-gesture-handler": {
|
||||
"version": "2.2.0",
|
||||
@@ -38782,37 +38805,42 @@
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-1.13.0.tgz",
|
||||
"integrity": "sha512-g8G5QURitDeC/zRlobDvUXLxKYfMlIM2HQNWJKbHPSu61qfs0djnK4s1NZuQzihkeAO0KJ4AS2XWvKBzUmlXtA==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-hw-keyboard-event": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-hw-keyboard-event/-/react-native-hw-keyboard-event-0.0.4.tgz",
|
||||
"integrity": "sha512-G8qp0nm17PHigLb/axgdF9xg51BKCG2p1AGeq//J/luLp5zNczIcQJh+nm02R1MeEUE3e53wqO4LMe0MV3raZg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-image-picker": {
|
||||
"version": "4.7.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-4.7.3.tgz",
|
||||
"integrity": "sha512-eRKm4wlsmZHmsWFyv77kYc2F+ZyEVqe0m7mqhsMzWk6TQT4FBDtEDxmRDDFq+ivCu/1QD+EPhmYcAIpeGr7Ekg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-incall-manager": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-3.3.0.tgz",
|
||||
"integrity": "sha512-SvomgHUoKqVso/BGv02b4ndBqenlqYqW3pptZz5qHwteidKFNWI6ny+PPw5X55MdK0lyTbzBoiT3DwS06Qu0lg==",
|
||||
"requires": {}
|
||||
"version": "git+ssh://git@github.com/cpoile/react-native-incall-manager.git#8c55b9dac0a2ab25d651fb54b504d384f9989b36",
|
||||
"from": "react-native-incall-manager@github:cpoile/react-native-incall-manager",
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-keyboard-aware-scrollview": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-keyboard-aware-scrollview/-/react-native-keyboard-aware-scrollview-2.1.0.tgz",
|
||||
"integrity": "sha512-XfWozWFPhdecfxN+wuqERX3mCGDrAim5siC6TWg3Qw7wK/zlwIwe1UIsHDNOQCzf9oIh0SkZXvoOFsMrnyIVmQ==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-keyboard-tracking-view": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-keyboard-tracking-view/-/react-native-keyboard-tracking-view-5.7.0.tgz",
|
||||
"integrity": "sha512-MDeEwAbn9LJDOfHq0QLCGaZirVLk2X/tHqkAqz3y6uxryTRdSl9PwleOVar5Jx2oAPEg4J9BXbUD1wwOOi+5Kg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-keychain": {
|
||||
"version": "8.0.0",
|
||||
@@ -38823,25 +38851,29 @@
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.5.6.tgz",
|
||||
"integrity": "sha512-HDwEaXcQIuXXCV70O+bK1rizFong3wj+5Q/jSyifKFLg0VWF95xh8XQgfzXwtq0NggL9vNjPKXa016KuFu+VFg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-local-auth": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-local-auth/-/react-native-local-auth-1.6.0.tgz",
|
||||
"integrity": "sha512-36cYGZGCG82pMiVJbQa5WMA93khP4v5JqLutFkMyB/eRpCULHmojNIBlbUPIY9SCeN4sg5VBRFTVGCtTg2r2kA==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-localize": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-2.1.9.tgz",
|
||||
"integrity": "sha512-n2Bi5P0cII8BrnG23Fs9PmpYcufD3JRbr1IBn3ArIA3Z3cGWg4eebv6am+CgdFC4kTPWUlVzX9fEgEMA+X8d6w==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-mmkv-storage": {
|
||||
"version": "0.6.11",
|
||||
"resolved": "https://registry.npmjs.org/react-native-mmkv-storage/-/react-native-mmkv-storage-0.6.11.tgz",
|
||||
"integrity": "sha512-PaxUxbTwr+PHfzoYt6E1Mds9/CjV5uOFVstuBW0laFUWlGrTHgiaLWR8VwJt0EPScSl5Lz4RM8IiqmXbIUSbbw==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-navigation": {
|
||||
"version": "7.25.1",
|
||||
@@ -38866,7 +38898,8 @@
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-4.1.3.tgz",
|
||||
"integrity": "sha512-A4SmRyfh2OlkptlJQvcQKkfnBKO1toUShmFplTkLXPNCqfpm/i4Fz+Uv+LzHSvbsU5U7EYf3JX9sfuyR06ZGPg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-passcode-status": {
|
||||
"version": "1.1.2",
|
||||
@@ -38877,7 +38910,8 @@
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-3.2.0.tgz",
|
||||
"integrity": "sha512-UPXxf2twjYL9vPI4HP2kT15AOTY489MhsNuyAgp+wJM2IRkkSVW6rO3k4WuSRL9ZmPhwkWb9bYjf8EEwRzZcXg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-ratings": {
|
||||
"version": "8.0.4",
|
||||
@@ -38935,7 +38969,8 @@
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
|
||||
"integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-screens": {
|
||||
"version": "3.10.2",
|
||||
@@ -38960,7 +38995,8 @@
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz",
|
||||
"integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-slider": {
|
||||
"version": "0.11.0",
|
||||
@@ -38974,7 +39010,8 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-startup-time/-/react-native-startup-time-2.0.0.tgz",
|
||||
"integrity": "sha512-tYruEDvwoVEOf+FMTTqp3aHNfA5ARWXXV+ar4wJBIQBdIlPEYDQWaYljOAV2dITuTsKAyPY2Q/N58gkKvGWIrA==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "12.1.1",
|
||||
@@ -39059,8 +39096,8 @@
|
||||
}
|
||||
},
|
||||
"react-native-webrtc": {
|
||||
"version": "git+ssh://git@github.com/streamer45/react-native-webrtc.git#7fe7d434892e6b29c5a6086d30b10f482db1e592",
|
||||
"from": "react-native-webrtc@github:streamer45/react-native-webrtc",
|
||||
"version": "git+ssh://git@github.com/mattermost/react-native-webrtc.git#7f765758f2f67e467ebd224c1c4d00a475e400d3",
|
||||
"from": "react-native-webrtc@github:mattermost/react-native-webrtc",
|
||||
"requires": {
|
||||
"base64-js": "^1.1.2",
|
||||
"event-target-shim": "^1.0.5",
|
||||
@@ -39339,7 +39376,8 @@
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-batched-actions/-/redux-batched-actions-0.5.0.tgz",
|
||||
"integrity": "sha512-6orZWyCnIQXMGY4DUGM0oj0L7oYnwTACsfsru/J7r94RM3P9eS7SORGpr3LCeRCMoIMQcpfKZ7X4NdyFHBS8Eg==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"redux-mock-store": {
|
||||
"version": "1.5.4",
|
||||
@@ -39354,7 +39392,8 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
|
||||
"integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"redux-persist-node-storage": {
|
||||
"version": "2.0.0",
|
||||
@@ -39383,7 +39422,8 @@
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
|
||||
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"reflect.ownkeys": {
|
||||
"version": "0.2.0",
|
||||
@@ -39726,7 +39766,8 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/rn-placeholder/-/rn-placeholder-3.0.3.tgz",
|
||||
"integrity": "sha512-EmVeLT8zDcTPilQZ2OHO/IiYUy2gApKGgbshDZBX0C4qxsn0cFATwgwOwyz8O7Vwg1Hul97Ci95hu7d6Js6XMQ==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"rst-selector-parser": {
|
||||
"version": "2.2.3",
|
||||
@@ -40042,7 +40083,8 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -40254,13 +40296,13 @@
|
||||
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
|
||||
},
|
||||
"simple-plist": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.0.tgz",
|
||||
"integrity": "sha512-uYWpeGFtZtVt2NhG4AHgpwx323zxD85x42heMJBan1qAiqqozIlaGrwrEt6kRjXWRWIXsuV1VLCvVmZan2B5dg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz",
|
||||
"integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==",
|
||||
"requires": {
|
||||
"bplist-creator": "0.1.0",
|
||||
"bplist-parser": "0.3.0",
|
||||
"plist": "^3.0.4"
|
||||
"bplist-parser": "0.3.1",
|
||||
"plist": "^3.0.5"
|
||||
}
|
||||
},
|
||||
"simple-swizzle": {
|
||||
@@ -41087,7 +41129,8 @@
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -41810,7 +41853,8 @@
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
@@ -42079,7 +42123,8 @@
|
||||
"version": "7.5.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz",
|
||||
"integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==",
|
||||
"requires": {}
|
||||
"requires": {
|
||||
}
|
||||
},
|
||||
"xcode": {
|
||||
"version": "3.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.50.1",
|
||||
"version": "1.51.2",
|
||||
"description": "Mattermost Mobile with React Native",
|
||||
"repository": "git@github.com:mattermost/mattermost-mobile.git",
|
||||
"author": "Mattermost, Inc.",
|
||||
@@ -25,7 +25,7 @@
|
||||
"array.prototype.flat": "1.2.5",
|
||||
"base-64": "1.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"commonmark": "github:mattermost/commonmark.js#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"commonmark": "github:mattermost/commonmark.js#d1003be97d15414af6c21894125623c45e3f5096",
|
||||
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"deep-equal": "2.0.5",
|
||||
"deepmerge": "4.2.2",
|
||||
@@ -55,7 +55,7 @@
|
||||
"react-native-haptic-feedback": "1.13.0",
|
||||
"react-native-hw-keyboard-event": "0.0.4",
|
||||
"react-native-image-picker": "4.7.3",
|
||||
"react-native-incall-manager": "3.3.0",
|
||||
"react-native-incall-manager": "github:cpoile/react-native-incall-manager",
|
||||
"react-native-keyboard-aware-scrollview": "2.1.0",
|
||||
"react-native-keyboard-tracking-view": "5.7.0",
|
||||
"react-native-keychain": "8.0.0",
|
||||
@@ -79,7 +79,7 @@
|
||||
"react-native-svg": "12.1.1",
|
||||
"react-native-vector-icons": "9.0.0",
|
||||
"react-native-video": "5.2.0",
|
||||
"react-native-webrtc": "github:streamer45/react-native-webrtc",
|
||||
"react-native-webrtc": "github:mattermost/react-native-webrtc",
|
||||
"react-native-webview": "11.17.1",
|
||||
"react-native-youtube": "2.0.2",
|
||||
"react-redux": "7.2.6",
|
||||
|
||||
67
types/modules/react-native-webrtc.d.ts
vendored
67
types/modules/react-native-webrtc.d.ts
vendored
@@ -50,11 +50,17 @@ declare module 'react-native-webrtc' {
|
||||
constructor();
|
||||
|
||||
stop(): void;
|
||||
|
||||
applyConstraints(): void;
|
||||
|
||||
clone(): void;
|
||||
|
||||
getCapabilities(): void;
|
||||
|
||||
getConstraints(): void;
|
||||
|
||||
getSettings(): void;
|
||||
|
||||
release(): void;
|
||||
|
||||
private _switchCamera(): void;
|
||||
@@ -74,13 +80,21 @@ declare module 'react-native-webrtc' {
|
||||
constructor(arg: any);
|
||||
|
||||
addTrack(track: MediaStreamTrack): void;
|
||||
|
||||
removeTrack(track: MediaStreamTrack): void;
|
||||
|
||||
getTracks(): MediaStreamTrack[];
|
||||
|
||||
getTrackById(trackId: string): MediaStreamTrack | undefined;
|
||||
|
||||
getAudioTracks(): MediaStreamTrack[];
|
||||
|
||||
getVideoTracks(): MediaStreamTrack[];
|
||||
|
||||
clone(): void;
|
||||
|
||||
toURL(): string;
|
||||
|
||||
release(): void;
|
||||
}
|
||||
|
||||
@@ -92,23 +106,27 @@ declare module 'react-native-webrtc' {
|
||||
bufferedAmountLowThreshold: number;
|
||||
id: number;
|
||||
label: string;
|
||||
maxPacketLifeTime: ?number;
|
||||
maxRetransmits: ?number;
|
||||
maxPacketLifeTime?: number;
|
||||
maxRetransmits?: number;
|
||||
negotiated: boolean;
|
||||
ordered: boolean;
|
||||
protocol: string;
|
||||
readyState: 'connecting' | 'open' | 'closing' | 'closed';
|
||||
|
||||
onopen: ?Function;
|
||||
onmessage: ?Function;
|
||||
onbufferedamountlow: ?Function;
|
||||
onerror: ?Function;
|
||||
onclose: ?Function;
|
||||
onopen?: Function;
|
||||
onmessage?: Function;
|
||||
onbufferedamountlow?: Function;
|
||||
onerror?: Function;
|
||||
onclose?: Function;
|
||||
|
||||
constructor(peerConnectionId: number, label: string, dataChannelDict: RTCDataChannelInit)
|
||||
|
||||
send(data: string | ArrayBuffer | ArrayBufferView): void
|
||||
|
||||
close(): void
|
||||
|
||||
_unregisterEvents(): void
|
||||
|
||||
_registerEvents(): void
|
||||
}
|
||||
|
||||
@@ -116,6 +134,7 @@ declare module 'react-native-webrtc' {
|
||||
type: string;
|
||||
data: string | ArrayBuffer | Blob;
|
||||
origin: string;
|
||||
|
||||
constructor(type: any, eventInitDict: any)
|
||||
}
|
||||
|
||||
@@ -129,6 +148,33 @@ declare module 'react-native-webrtc' {
|
||||
track?: MediaStreamTrack;
|
||||
}
|
||||
|
||||
export interface EventOnConnectionStateChange {
|
||||
target: {
|
||||
iceConnectionState: RTCIceConnectionState;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConfigurationParam {
|
||||
username?: string | undefined;
|
||||
credential?: string | undefined;
|
||||
}
|
||||
|
||||
export interface ConfigurationParamWithUrls extends ConfigurationParam {
|
||||
urls: string[];
|
||||
}
|
||||
|
||||
export interface ConfigurationParamWithUrl extends ConfigurationParam {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface RTCPeerConnectionConfiguration {
|
||||
iceServers: ConfigurationParamWithUrls[] | ConfigurationParamWithUrl[];
|
||||
iceTransportPolicy?: 'all' | 'relay' | 'nohost' | 'none' | undefined;
|
||||
bundlePolicy?: 'balanced' | 'max-compat' | 'max-bundle' | undefined;
|
||||
rtcpMuxPolicy?: 'negotiate' | 'require' | undefined;
|
||||
iceCandidatePoolSize?: number | undefined;
|
||||
}
|
||||
|
||||
export class RTCPeerConnection {
|
||||
localDescription: RTCSessionDescriptionType;
|
||||
remoteDescription: RTCSessionDescriptionType;
|
||||
@@ -164,11 +210,11 @@ declare module 'react-native-webrtc' {
|
||||
|
||||
addTrack(track: MediaStreamTrack): void;
|
||||
|
||||
addTransceiver(kind: 'audio'|'video'|MediaStreamTrack, init: any): void;
|
||||
addTransceiver(kind: 'audio' | 'video' | MediaStreamTrack, init: any): void;
|
||||
|
||||
removeStream(stream: MediaStream): void;
|
||||
|
||||
removeTrack(sender: RtpSender): Promise<boolean>
|
||||
removeTrack(sender: RTCRtpSender): Promise<boolean>
|
||||
|
||||
createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionType>;
|
||||
|
||||
@@ -188,7 +234,7 @@ declare module 'react-native-webrtc' {
|
||||
|
||||
getRemoteStreams(): MediaStream[];
|
||||
|
||||
close(): void;
|
||||
close(cb?: () => void): void;
|
||||
|
||||
private _getTrack(streamReactTag: string, trackId: string): MediaStreamTrack;
|
||||
|
||||
@@ -218,6 +264,7 @@ declare module 'react-native-webrtc' {
|
||||
|
||||
export class RTCSessionDescription extends RTCSessionDescriptionType {
|
||||
constructor(info: RTCSessionDescriptionType);
|
||||
|
||||
toJSON(): RTCSessionDescriptionType;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user