forked from Ivasoft/mattermost-mobile
Compare commits
6 Commits
v1
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d965ef9e1e | ||
|
|
b13ed0f409 | ||
|
|
a046e70ba8 | ||
|
|
20d106b609 | ||
|
|
b476fc00ff | ||
|
|
80357fa57b |
36
NOTICE.txt
36
NOTICE.txt
@@ -2273,6 +2273,42 @@ SOFTWARE.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## react-native-math-view
|
||||||
|
|
||||||
|
This product contains 'react-native-math-view' by Shachar.
|
||||||
|
|
||||||
|
A react native view used to easily display and handle math. The library doesn't use WebView.
|
||||||
|
|
||||||
|
* HOMEPAGE:
|
||||||
|
* https://github.com/ShaMan123/react-native-math-view
|
||||||
|
|
||||||
|
* LICENSE: MIT
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 ShaMan123
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
© 2022 GitHub, Inc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## react-native-mmkv-storage
|
## react-native-mmkv-storage
|
||||||
|
|
||||||
This product contains 'react-native-mmkv-storage' by Ammar Ahmed.
|
This product contains 'react-native-mmkv-storage' by Ammar Ahmed.
|
||||||
|
|||||||
@@ -131,8 +131,8 @@ android {
|
|||||||
applicationId "com.mattermost.rnbeta"
|
applicationId "com.mattermost.rnbeta"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 392
|
versionCode 398
|
||||||
versionName "1.51.1"
|
versionName "1.52.0"
|
||||||
multiDexEnabled = true
|
multiDexEnabled = true
|
||||||
testBuildType System.getProperty('testBuildType', 'debug')
|
testBuildType System.getProperty('testBuildType', 'debug')
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import {RNFetchBlobFetchRepsonse} from 'rn-fetch-blob';
|
import {RNFetchBlobFetchRepsonse} from 'rn-fetch-blob';
|
||||||
import urlParse from 'url-parse';
|
import urlParse from 'url-parse';
|
||||||
|
|
||||||
|
import Calls from '@constants/calls';
|
||||||
import {Options} from '@mm-redux/types/client4';
|
import {Options} from '@mm-redux/types/client4';
|
||||||
|
|
||||||
import * as ClientConstants from './constants';
|
import * as ClientConstants from './constants';
|
||||||
@@ -286,12 +287,16 @@ export default class ClientBase {
|
|||||||
return `${this.getUserThreadsRoute(userId, teamId)}/${threadId}`;
|
return `${this.getUserThreadsRoute(userId, teamId)}/${threadId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPluginsRoute() {
|
||||||
|
return `${this.getBaseRoute()}/plugins`;
|
||||||
|
}
|
||||||
|
|
||||||
getAppsProxyRoute() {
|
getAppsProxyRoute() {
|
||||||
return `${this.url}/plugins/com.mattermost.apps`;
|
return `${this.url}/plugins/com.mattermost.apps`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCallsRoute() {
|
getCallsRoute() {
|
||||||
return `${this.url}/plugins/com.mattermost.calls`;
|
return `${this.url}/plugins/${Calls.PluginId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client Helpers
|
// Client Helpers
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import ClientPlugins, {ClientPluginsMix} from '@client/rest/plugins';
|
||||||
import ClientCalls, {ClientCallsMix} from '@mmproducts/calls/client/rest';
|
import ClientCalls, {ClientCallsMix} from '@mmproducts/calls/client/rest';
|
||||||
import mix from '@utils/mix';
|
import mix from '@utils/mix';
|
||||||
|
|
||||||
@@ -36,7 +37,8 @@ interface Client extends ClientBase,
|
|||||||
ClientTeamsMix,
|
ClientTeamsMix,
|
||||||
ClientTosMix,
|
ClientTosMix,
|
||||||
ClientUsersMix,
|
ClientUsersMix,
|
||||||
ClientCallsMix
|
ClientCallsMix,
|
||||||
|
ClientPluginsMix
|
||||||
{}
|
{}
|
||||||
|
|
||||||
class Client extends mix(ClientBase).with(
|
class Client extends mix(ClientBase).with(
|
||||||
@@ -55,6 +57,7 @@ class Client extends mix(ClientBase).with(
|
|||||||
ClientTos,
|
ClientTos,
|
||||||
ClientUsers,
|
ClientUsers,
|
||||||
ClientCalls,
|
ClientCalls,
|
||||||
|
ClientPlugins,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
const Client4 = new Client();
|
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;
|
||||||
@@ -10,4 +10,6 @@ const RequiredServer = {
|
|||||||
PATCH_VERSION: 0,
|
PATCH_VERSION: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {RequiredServer, RefreshConfigMillis};
|
const PluginId = 'com.mattermost.calls';
|
||||||
|
|
||||||
|
export default {RequiredServer, RefreshConfigMillis, PluginId};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import Calls from '@constants/calls';
|
||||||
|
|
||||||
const WebsocketEvents = {
|
const WebsocketEvents = {
|
||||||
POSTED: 'posted',
|
POSTED: 'posted',
|
||||||
POST_EDITED: 'post_edited',
|
POST_EDITED: 'post_edited',
|
||||||
@@ -53,18 +56,18 @@ const WebsocketEvents = {
|
|||||||
SIDEBAR_CATEGORY_UPDATED: 'sidebar_category_updated',
|
SIDEBAR_CATEGORY_UPDATED: 'sidebar_category_updated',
|
||||||
SIDEBAR_CATEGORY_DELETED: 'sidebar_category_deleted',
|
SIDEBAR_CATEGORY_DELETED: 'sidebar_category_deleted',
|
||||||
SIDEBAR_CATEGORY_ORDER_UPDATED: 'sidebar_category_order_updated',
|
SIDEBAR_CATEGORY_ORDER_UPDATED: 'sidebar_category_order_updated',
|
||||||
CALLS_CHANNEL_ENABLED: 'custom_com.mattermost.calls_channel_enable_voice',
|
CALLS_CHANNEL_ENABLED: `custom_${Calls.PluginId}_channel_enable_voice`,
|
||||||
CALLS_CHANNEL_DISABLED: 'custom_com.mattermost.calls_channel_disable_voice',
|
CALLS_CHANNEL_DISABLED: `custom_${Calls.PluginId}_channel_disable_voice`,
|
||||||
CALLS_USER_CONNECTED: 'custom_com.mattermost.calls_user_connected',
|
CALLS_USER_CONNECTED: `custom_${Calls.PluginId}_user_connected`,
|
||||||
CALLS_USER_DISCONNECTED: 'custom_com.mattermost.calls_user_disconnected',
|
CALLS_USER_DISCONNECTED: `custom_${Calls.PluginId}_user_disconnected`,
|
||||||
CALLS_USER_MUTED: 'custom_com.mattermost.calls_user_muted',
|
CALLS_USER_MUTED: `custom_${Calls.PluginId}_user_muted`,
|
||||||
CALLS_USER_UNMUTED: 'custom_com.mattermost.calls_user_unmuted',
|
CALLS_USER_UNMUTED: `custom_${Calls.PluginId}_user_unmuted`,
|
||||||
CALLS_USER_VOICE_ON: 'custom_com.mattermost.calls_user_voice_on',
|
CALLS_USER_VOICE_ON: `custom_${Calls.PluginId}_user_voice_on`,
|
||||||
CALLS_USER_VOICE_OFF: 'custom_com.mattermost.calls_user_voice_off',
|
CALLS_USER_VOICE_OFF: `custom_${Calls.PluginId}_user_voice_off`,
|
||||||
CALLS_CALL_START: 'custom_com.mattermost.calls_call_start',
|
CALLS_CALL_START: `custom_${Calls.PluginId}_call_start`,
|
||||||
CALLS_SCREEN_ON: 'custom_com.mattermost.calls_user_screen_on',
|
CALLS_SCREEN_ON: `custom_${Calls.PluginId}_user_screen_on`,
|
||||||
CALLS_SCREEN_OFF: 'custom_com.mattermost.calls_user_screen_off',
|
CALLS_SCREEN_OFF: `custom_${Calls.PluginId}_user_screen_off`,
|
||||||
CALLS_USER_RAISE_HAND: 'custom_com.mattermost.calls_user_raise_hand',
|
CALLS_USER_RAISE_HAND: `custom_${Calls.PluginId}_user_raise_hand`,
|
||||||
CALLS_USER_UNRAISE_HAND: 'custom_com.mattermost.calls_user_unraise_hand',
|
CALLS_USER_UNRAISE_HAND: `custom_${Calls.PluginId}_user_unraise_hand`,
|
||||||
};
|
};
|
||||||
export default WebsocketEvents;
|
export default WebsocketEvents;
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ import {getCurrentChannel} from '@mm-redux/selectors/entities/channels';
|
|||||||
import {getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
|
import {getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
|
||||||
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin} from '@mm-redux/utils/user_utils';
|
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin} from '@mm-redux/utils/user_utils';
|
||||||
import {loadConfig} from '@mmproducts/calls/store/actions/calls';
|
import {loadConfig} from '@mmproducts/calls/store/actions/calls';
|
||||||
import {getConfig, isCallsExplicitlyDisabled, isCallsExplicitlyEnabled} from '@mmproducts/calls/store/selectors/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
|
// Check if calls is enabled. If it is, then run fn; if it isn't, show an alert and set
|
||||||
// msgPostfix to ' (Not Available)'.
|
// msgPostfix to ' (Not Available)'.
|
||||||
@@ -45,12 +50,15 @@ export const useCallsChannelSettings = () => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const config = useSelector(getConfig);
|
const config = useSelector(getConfig);
|
||||||
const currentChannel = useSelector(getCurrentChannel);
|
const currentChannel = useSelector(getCurrentChannel);
|
||||||
|
const pluginEnabled = useSelector(isCallsPluginEnabled);
|
||||||
const explicitlyDisabled = useSelector(isCallsExplicitlyDisabled);
|
const explicitlyDisabled = useSelector(isCallsExplicitlyDisabled);
|
||||||
const explicitlyEnabled = useSelector(isCallsExplicitlyEnabled);
|
const explicitlyEnabled = useSelector(isCallsExplicitlyEnabled);
|
||||||
const roles = useSelector(getCurrentUserRoles);
|
const roles = useSelector(getCurrentUserRoles);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(loadConfig());
|
if (pluginEnabled) {
|
||||||
|
dispatch(loadConfig());
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isDirectMessage = currentChannel.type === General.DM_CHANNEL;
|
const isDirectMessage = currentChannel.type === General.DM_CHANNEL;
|
||||||
@@ -58,9 +66,11 @@ export const useCallsChannelSettings = () => {
|
|||||||
const isAdmin = checkIsAdmin(roles);
|
const isAdmin = checkIsAdmin(roles);
|
||||||
const isChannelAdmin = isAdmin || checkIsChannelAdmin(roles);
|
const isChannelAdmin = isAdmin || checkIsChannelAdmin(roles);
|
||||||
|
|
||||||
const enabled = (explicitlyEnabled || (!explicitlyDisabled && config.DefaultEnabled));
|
const enabled = pluginEnabled && (explicitlyEnabled || (!explicitlyDisabled && config.DefaultEnabled));
|
||||||
let canEnableDisable;
|
let canEnableDisable;
|
||||||
if (config.AllowEnableCalls) {
|
if (!pluginEnabled) {
|
||||||
|
canEnableDisable = false;
|
||||||
|
} else if (config.AllowEnableCalls) {
|
||||||
canEnableDisable = isDirectMessage || isGroupMessage || isChannelAdmin;
|
canEnableDisable = isDirectMessage || isGroupMessage || isChannelAdmin;
|
||||||
} else {
|
} else {
|
||||||
canEnableDisable = isAdmin;
|
canEnableDisable = isAdmin;
|
||||||
|
|||||||
@@ -24,4 +24,5 @@ export default keyMirror({
|
|||||||
SET_SCREENSHARE_URL: null,
|
SET_SCREENSHARE_URL: null,
|
||||||
SET_SPEAKERPHONE: null,
|
SET_SPEAKERPHONE: null,
|
||||||
RECEIVED_CONFIG: null,
|
RECEIVED_CONFIG: null,
|
||||||
|
RECEIVED_PLUGIN_ENABLED: null,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ jest.mock('@client/rest', () => ({
|
|||||||
DefaultEnabled: true,
|
DefaultEnabled: true,
|
||||||
last_retrieved_at: 1234,
|
last_retrieved_at: 1234,
|
||||||
})),
|
})),
|
||||||
|
getPluginsManifests: jest.fn(() => (
|
||||||
|
[
|
||||||
|
{id: 'playbooks'},
|
||||||
|
{id: 'com.mattermost.calls'},
|
||||||
|
]
|
||||||
|
)),
|
||||||
enableChannelCalls: jest.fn(() => null),
|
enableChannelCalls: jest.fn(() => null),
|
||||||
disableChannelCalls: jest.fn(() => null),
|
disableChannelCalls: jest.fn(() => null),
|
||||||
},
|
},
|
||||||
@@ -135,14 +141,14 @@ describe('Actions.Calls', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('loadCalls', async () => {
|
it('loadCalls', async () => {
|
||||||
await store.dispatch(await store.dispatch(CallsActions.loadCalls()));
|
await store.dispatch(CallsActions.loadCalls());
|
||||||
expect(Client4.getCalls).toBeCalledWith();
|
expect(Client4.getCalls).toBeCalledWith();
|
||||||
assert.equal(store.getState().entities.calls.calls['channel-1'].channelId, 'channel-1');
|
assert.equal(store.getState().entities.calls.calls['channel-1'].channelId, 'channel-1');
|
||||||
assert.equal(store.getState().entities.calls.enabled['channel-1'], true);
|
assert.equal(store.getState().entities.calls.enabled['channel-1'], true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loadConfig', async () => {
|
it('loadConfig', async () => {
|
||||||
await store.dispatch(await store.dispatch(CallsActions.loadConfig()));
|
await store.dispatch(CallsActions.loadConfig());
|
||||||
expect(Client4.getCallsConfig).toBeCalledWith();
|
expect(Client4.getCallsConfig).toBeCalledWith();
|
||||||
assert.equal(store.getState().entities.calls.config.DefaultEnabled, true);
|
assert.equal(store.getState().entities.calls.config.DefaultEnabled, true);
|
||||||
assert.equal(store.getState().entities.calls.config.AllowEnableCalls, true);
|
assert.equal(store.getState().entities.calls.config.AllowEnableCalls, true);
|
||||||
@@ -150,6 +156,7 @@ describe('Actions.Calls', () => {
|
|||||||
|
|
||||||
it('batchLoadConfig', async () => {
|
it('batchLoadConfig', async () => {
|
||||||
await store.dispatch(CallsActions.batchLoadCalls());
|
await store.dispatch(CallsActions.batchLoadCalls());
|
||||||
|
expect(Client4.getPluginsManifests).toBeCalledWith();
|
||||||
expect(Client4.getCallsConfig).toBeCalledWith();
|
expect(Client4.getCallsConfig).toBeCalledWith();
|
||||||
expect(Client4.getCalls).toBeCalledWith();
|
expect(Client4.getCalls).toBeCalledWith();
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,19 @@
|
|||||||
|
|
||||||
import {intlShape} from 'react-intl';
|
import {intlShape} from 'react-intl';
|
||||||
import InCallManager from 'react-native-incall-manager';
|
import InCallManager from 'react-native-incall-manager';
|
||||||
|
import {batch} from 'react-redux';
|
||||||
|
|
||||||
import {Client4} from '@client/rest';
|
import {Client4} from '@client/rest';
|
||||||
import Calls from '@constants/calls';
|
import Calls from '@constants/calls';
|
||||||
import {logError} from '@mm-redux/actions/errors';
|
import {logError} from '@mm-redux/actions/errors';
|
||||||
import {forceLogoutIfNecessary} from '@mm-redux/actions/helpers';
|
import {forceLogoutIfNecessary} from '@mm-redux/actions/helpers';
|
||||||
import {GenericAction, ActionFunc, DispatchFunc, GetStateFunc, batchActions} from '@mm-redux/types/actions';
|
import {
|
||||||
|
GenericAction,
|
||||||
|
ActionFunc,
|
||||||
|
DispatchFunc,
|
||||||
|
GetStateFunc,
|
||||||
|
ActionResult,
|
||||||
|
} from '@mm-redux/types/actions';
|
||||||
import {Dictionary} from '@mm-redux/types/utilities';
|
import {Dictionary} from '@mm-redux/types/utilities';
|
||||||
import {newClient} from '@mmproducts/calls/connection';
|
import {newClient} from '@mmproducts/calls/connection';
|
||||||
import CallsTypes from '@mmproducts/calls/store/action_types/calls';
|
import CallsTypes from '@mmproducts/calls/store/action_types/calls';
|
||||||
@@ -19,7 +26,7 @@ import {hasMicrophonePermission} from '@utils/permission';
|
|||||||
export let ws: any = null;
|
export let ws: any = null;
|
||||||
|
|
||||||
export function loadConfig(force = false): ActionFunc {
|
export function loadConfig(force = false): ActionFunc {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<GenericAction> => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
if ((Date.now() - getConfig(getState()).last_retrieved_at) < Calls.RefreshConfigMillis) {
|
if ((Date.now() - getConfig(getState()).last_retrieved_at) < Calls.RefreshConfigMillis) {
|
||||||
return {} as GenericAction;
|
return {} as GenericAction;
|
||||||
@@ -34,28 +41,27 @@ export function loadConfig(force = false): ActionFunc {
|
|||||||
dispatch(logError(error));
|
dispatch(logError(error));
|
||||||
|
|
||||||
// Reset the config to the default (off) since it looks like Calls is not enabled.
|
// Reset the config to the default (off) since it looks like Calls is not enabled.
|
||||||
return {
|
dispatch({
|
||||||
type: CallsTypes.RECEIVED_CONFIG,
|
type: CallsTypes.RECEIVED_CONFIG,
|
||||||
data: {...DefaultServerConfig, last_retrieved_at: Date.now()},
|
data: {...DefaultServerConfig, last_retrieved_at: Date.now()},
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
data = {...data, last_retrieved_at: Date.now()};
|
||||||
type: CallsTypes.RECEIVED_CONFIG,
|
dispatch({type: CallsTypes.RECEIVED_CONFIG, data});
|
||||||
data: {...data, last_retrieved_at: Date.now()},
|
return {data};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadCalls(): ActionFunc {
|
export function loadCalls(): ActionFunc {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<GenericAction> => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||||
let resp = [];
|
let resp = [];
|
||||||
try {
|
try {
|
||||||
resp = await Client4.getCalls();
|
resp = await Client4.getCalls();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
forceLogoutIfNecessary(error, dispatch, getState);
|
forceLogoutIfNecessary(error, dispatch, getState);
|
||||||
dispatch(logError(error));
|
dispatch(logError(error));
|
||||||
return {} as GenericAction;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const callsResults: Dictionary<Call> = {};
|
const callsResults: Dictionary<Call> = {};
|
||||||
@@ -86,21 +92,46 @@ export function loadCalls(): ActionFunc {
|
|||||||
enabled: enabledChannels,
|
enabled: enabledChannels,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {type: CallsTypes.RECEIVED_CALLS, data};
|
dispatch({type: CallsTypes.RECEIVED_CALLS, data});
|
||||||
|
return {data};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function batchLoadCalls(forceConfig = false): ActionFunc {
|
export function batchLoadCalls(forceConfig = false): ActionFunc {
|
||||||
return async (dispatch: DispatchFunc) => {
|
return async (dispatch: DispatchFunc) => {
|
||||||
const promises = [dispatch(loadConfig(forceConfig)), dispatch(loadCalls())];
|
const res = await dispatch(checkIsCallsPluginEnabled());
|
||||||
Promise.all(promises).then((actions: Array<Awaited<GenericAction>>) => {
|
if (!res.data) {
|
||||||
dispatch(batchActions(actions, 'BATCH_LOAD_CALLS'));
|
// Calls is not enabled.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
batch(() => {
|
||||||
|
dispatch(loadConfig(forceConfig));
|
||||||
|
dispatch(loadCalls());
|
||||||
});
|
});
|
||||||
|
|
||||||
return {};
|
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 {
|
export function enableChannelCalls(channelId: string): ActionFunc {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
try {
|
try {
|
||||||
@@ -135,6 +166,10 @@ export function disableChannelCalls(channelId: string): ActionFunc {
|
|||||||
|
|
||||||
export function joinCall(channelId: string, intl: typeof intlShape): ActionFunc {
|
export function joinCall(channelId: string, intl: typeof intlShape): ActionFunc {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
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);
|
const hasPermission = await hasMicrophonePermission(intl);
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
return {error: 'no permissions to microphone, unable to start call'};
|
return {error: 'no permissions to microphone, unable to start call'};
|
||||||
|
|||||||
@@ -216,6 +216,16 @@ 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({
|
export default combineReducers({
|
||||||
calls,
|
calls,
|
||||||
enabled,
|
enabled,
|
||||||
@@ -223,4 +233,5 @@ export default combineReducers({
|
|||||||
screenShareURL,
|
screenShareURL,
|
||||||
speakerphoneOn,
|
speakerphoneOn,
|
||||||
config,
|
config,
|
||||||
|
pluginEnabled,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,3 +61,7 @@ export function isSupportedServer(state: GlobalState) {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCallsPluginEnabled(state: GlobalState) {
|
||||||
|
return state.entities.calls.pluginEnabled;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export type CallsState = {
|
|||||||
screenShareURL: string;
|
screenShareURL: string;
|
||||||
speakerphoneOn: boolean;
|
speakerphoneOn: boolean;
|
||||||
config: ServerConfig;
|
config: ServerConfig;
|
||||||
|
pluginEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Call = {
|
export type Call = {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
|
|
||||||
|
import Calls from '@constants/calls';
|
||||||
import {encode} from '@msgpack/msgpack/dist';
|
import {encode} from '@msgpack/msgpack/dist';
|
||||||
|
|
||||||
export default class WebSocketClient extends EventEmitter {
|
export default class WebSocketClient extends EventEmitter {
|
||||||
private ws: WebSocket | null;
|
private ws: WebSocket | null;
|
||||||
private seqNo = 0;
|
private seqNo = 0;
|
||||||
private connID = '';
|
private connID = '';
|
||||||
private eventPrefix = 'custom_com.mattermost.calls';
|
private eventPrefix = `custom_${Calls.PluginId}`;
|
||||||
|
|
||||||
constructor(connURL: string) {
|
constructor(connURL: string) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default class ChannelAndroid extends ChannelBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {theme, viewingGlobalThreads, isSupportedServerCalls} = this.props;
|
const {theme, viewingGlobalThreads, isCallsEnabled} = this.props;
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
if (viewingGlobalThreads) {
|
if (viewingGlobalThreads) {
|
||||||
@@ -106,11 +106,12 @@ export default class ChannelAndroid extends ChannelBase {
|
|||||||
{component}
|
{component}
|
||||||
<NetworkIndicator/>
|
<NetworkIndicator/>
|
||||||
<AnnouncementBanner/>
|
<AnnouncementBanner/>
|
||||||
{isSupportedServerCalls &&
|
{isCallsEnabled &&
|
||||||
<FloatingCallContainer>
|
<FloatingCallContainer>
|
||||||
<JoinCall/>
|
<JoinCall/>
|
||||||
<CurrentCall/>
|
<CurrentCall/>
|
||||||
</FloatingCallContainer>}
|
</FloatingCallContainer>
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default class ChannelIOS extends ChannelBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {currentChannelId, theme, viewingGlobalThreads, isSupportedServerCalls} = this.props;
|
const {currentChannelId, theme, viewingGlobalThreads, isCallsEnabled} = this.props;
|
||||||
|
|
||||||
let component;
|
let component;
|
||||||
let renderDraftArea = false;
|
let renderDraftArea = false;
|
||||||
@@ -85,11 +85,12 @@ export default class ChannelIOS extends ChannelBase {
|
|||||||
<>
|
<>
|
||||||
<AnnouncementBanner/>
|
<AnnouncementBanner/>
|
||||||
<NetworkIndicator/>
|
<NetworkIndicator/>
|
||||||
{isSupportedServerCalls &&
|
{isCallsEnabled &&
|
||||||
<FloatingCallContainer>
|
<FloatingCallContainer>
|
||||||
<JoinCall/>
|
<JoinCall/>
|
||||||
<CurrentCall/>
|
<CurrentCall/>
|
||||||
</FloatingCallContainer>}
|
</FloatingCallContainer>
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
const header = (
|
const header = (
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export default class ChannelBase extends PureComponent {
|
|||||||
viewingGlobalThreads: PropTypes.bool,
|
viewingGlobalThreads: PropTypes.bool,
|
||||||
collapsedThreadsEnabled: PropTypes.bool.isRequired,
|
collapsedThreadsEnabled: PropTypes.bool.isRequired,
|
||||||
isSupportedServerCalls: PropTypes.bool.isRequired,
|
isSupportedServerCalls: PropTypes.bool.isRequired,
|
||||||
|
isCallsEnabled: PropTypes.bool.isRequired,
|
||||||
selectedPost: PropTypes.shape({
|
selectedPost: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
channel_id: PropTypes.string.isRequired,
|
channel_id: PropTypes.string.isRequired,
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ import {getCurrentUserId, getCurrentUserRoles, shouldShowTermsOfService} from '@
|
|||||||
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
|
||||||
import {isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
|
import {isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
|
||||||
import {batchLoadCalls} from '@mmproducts/calls/store/actions/calls';
|
import {batchLoadCalls} from '@mmproducts/calls/store/actions/calls';
|
||||||
import {isSupportedServer as isSupportedServerForCalls} from '@mmproducts/calls/store/selectors/calls';
|
import {
|
||||||
|
isCallsPluginEnabled,
|
||||||
|
isSupportedServer as isSupportedServerForCalls,
|
||||||
|
} from '@mmproducts/calls/store/selectors/calls';
|
||||||
import {getViewingGlobalThreads} from '@selectors/threads';
|
import {getViewingGlobalThreads} from '@selectors/threads';
|
||||||
|
|
||||||
import Channel from './channel';
|
import Channel from './channel';
|
||||||
@@ -44,6 +47,7 @@ function mapStateToProps(state) {
|
|||||||
const currentChannelId = currentTeam?.delete_at === 0 ? getCurrentChannelId(state) : '';
|
const currentChannelId = currentTeam?.delete_at === 0 ? getCurrentChannelId(state) : '';
|
||||||
const collapsedThreadsEnabled = isCollapsedThreadsEnabled(state);
|
const collapsedThreadsEnabled = isCollapsedThreadsEnabled(state);
|
||||||
const isSupportedServerCalls = isSupportedServerForCalls(state);
|
const isSupportedServerCalls = isSupportedServerForCalls(state);
|
||||||
|
const isCallsEnabled = isCallsPluginEnabled(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentChannelId,
|
currentChannelId,
|
||||||
@@ -58,6 +62,7 @@ function mapStateToProps(state) {
|
|||||||
theme: getTheme(state),
|
theme: getTheme(state),
|
||||||
viewingGlobalThreads: collapsedThreadsEnabled && getViewingGlobalThreads(state),
|
viewingGlobalThreads: collapsedThreadsEnabled && getViewingGlobalThreads(state),
|
||||||
isSupportedServerCalls,
|
isSupportedServerCalls,
|
||||||
|
isCallsEnabled,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ GEM
|
|||||||
artifactory (3.0.15)
|
artifactory (3.0.15)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.579.0)
|
aws-partitions (1.581.0)
|
||||||
aws-sdk-core (3.130.1)
|
aws-sdk-core (3.130.2)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.525.0)
|
aws-partitions (~> 1, >= 1.525.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
@@ -17,7 +17,7 @@ GEM
|
|||||||
aws-sdk-kms (1.56.0)
|
aws-sdk-kms (1.56.0)
|
||||||
aws-sdk-core (~> 3, >= 3.127.0)
|
aws-sdk-core (~> 3, >= 3.127.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.113.0)
|
aws-sdk-s3 (1.113.2)
|
||||||
aws-sdk-core (~> 3, >= 3.127.0)
|
aws-sdk-core (~> 3, >= 3.127.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.4)
|
aws-sigv4 (~> 1.4)
|
||||||
@@ -36,7 +36,7 @@ GEM
|
|||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.7.6)
|
dotenv (2.7.6)
|
||||||
emoji_regex (3.2.3)
|
emoji_regex (3.2.3)
|
||||||
excon (0.92.2)
|
excon (0.92.3)
|
||||||
faraday (1.10.0)
|
faraday (1.10.0)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
|
|||||||
@@ -911,7 +911,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 392;
|
CURRENT_PROJECT_VERSION = 398;
|
||||||
DEAD_CODE_STRIPPING = NO;
|
DEAD_CODE_STRIPPING = NO;
|
||||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -953,7 +953,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 392;
|
CURRENT_PROJECT_VERSION = 398;
|
||||||
DEAD_CODE_STRIPPING = NO;
|
DEAD_CODE_STRIPPING = NO;
|
||||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.51.1</string>
|
<string>1.52.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>392</string>
|
<string>398</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.51.1</string>
|
<string>1.52.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>392</string>
|
<string>398</string>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.51.1</string>
|
<string>1.52.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>392</string>
|
<string>398</string>
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
|||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mattermost-mobile",
|
"name": "mattermost-mobile",
|
||||||
"version": "1.51.1",
|
"version": "1.52.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mattermost-mobile",
|
"name": "mattermost-mobile",
|
||||||
"version": "1.51.1",
|
"version": "1.52.0",
|
||||||
"description": "Mattermost Mobile with React Native",
|
"description": "Mattermost Mobile with React Native",
|
||||||
"repository": "git@github.com:mattermost/mattermost-mobile.git",
|
"repository": "git@github.com:mattermost/mattermost-mobile.git",
|
||||||
"author": "Mattermost, Inc.",
|
"author": "Mattermost, Inc.",
|
||||||
|
|||||||
Reference in New Issue
Block a user