Compare commits

...

10 Commits

Author SHA1 Message Date
hmuresan
bf5c37b6f8 feat (external_api) add command for kick participant 2021-02-11 13:29:34 -06:00
Vlad Piersec
e22e8b9d07 fix(vpaas): Store billing id in parent lolcaStorage on Safari 2021-01-19 12:31:10 -06:00
Tudor-Ovidiu Avram
73971dfb26 fix(subject) remove gradient if no info in topbar 2021-01-19 12:31:03 -06:00
hmuresan
7012b49c17 feat(external-api) set privateMessage flag on incoming-message 2021-01-19 12:30:54 -06:00
Tudor-Ovidiu Avram
a11171e7e9 fix(jaas) disable directory integration 2021-01-19 12:30:41 -06:00
hmuresan
76b96e2882 feat(external_api) add command and event listener for CS 2021-01-19 12:30:34 -06:00
Tudor-Ovidiu Avram
5a9c6c1d80 feat(external_api) allow notifications to be configured 2021-01-19 12:30:26 -06:00
Tudor-Ovidiu Avram
4b15dddfe5 feat(external_api) allow initial gUM requests to be disabled 2021-01-19 12:30:19 -06:00
hmuresan
c16f4c415a feat(external_api) set and cancel private chat through external API
- allow managing chat through API when chat button is not present on UI
2021-01-19 12:30:07 -06:00
damencho
730fff02cd feat: Updates ljm for 4628 release with few changes.
fix: Disabling lobby when using tenant.
7896fc8b92

feat: Detects shard changed when using websockets.
cb484cf48c

feat: Changes ws default keepalive interval.
1d881f4964

fix: Skip header checking for ws and shard when not configured.
86bc5122a6
2021-01-19 12:11:11 -06:00
25 changed files with 255 additions and 122 deletions

View File

@@ -472,8 +472,8 @@ export default {
*/
createInitialLocalTracks(options = {}) {
const errors = {};
const initialDevices = [ 'audio' ];
const requestedAudio = true;
const initialDevices = config.disableInitialGUM ? [] : [ 'audio' ];
const requestedAudio = !config.disableInitialGUM;
let requestedVideo = false;
// Always get a handle on the audio input device so that we have statistics even if the user joins the
@@ -484,19 +484,22 @@ export default {
this.muteAudio(true, true);
}
if (!options.startWithVideoMuted
if (!config.disableInitialGUM
&& !options.startWithVideoMuted
&& !options.startAudioOnly
&& !options.startScreenSharing) {
initialDevices.push('video');
requestedVideo = true;
}
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
browserName =>
APP.store.dispatch(
mediaPermissionPromptVisibilityChanged(true, browserName))
);
if (!config.disableInitialGUM) {
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
browserName =>
APP.store.dispatch(
mediaPermissionPromptVisibilityChanged(true, browserName))
);
}
let tryCreateLocalTracks;

View File

@@ -336,6 +336,10 @@ var config = {
// will be joined when no room is specified.
enableWelcomePage: true,
// Disable initial browser getUserMedia requests.
// This is useful for scenarios where users might want to start a conference for screensharing only
// disableInitialGUM: false,
// Enabling the close page will ignore the welcome page redirection when
// a call is hangup.
// enableClosePage: false,
@@ -691,6 +695,65 @@ var config = {
ignoreStartMuted
*/
/**
Use this array to configure which notifications will be shown to the user
The items correspond to the title or description key of that notification
Some of these notifications also depend on some other internal logic to be displayed or not,
so adding them here will not ensure they will always be displayed
A falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false)
*/
// notifications: [
// 'connection.CONNFAIL', // shown when the connection fails,
// 'dialog.cameraNotSendingData', // shown when there's no feed from user's camera
// 'dialog.kickTitle', // shown when user has been kicked
// 'dialog.liveStreaming', // livestreaming notifications (pending, on, off, limits)
// 'dialog.lockTitle', // shown when setting conference password fails
// 'dialog.maxUsersLimitReached', // shown when maximmum users limit has been reached
// 'dialog.micNotSendingData', // shown when user's mic is not sending any audio
// 'dialog.passwordNotSupportedTitle', // shown when setting conference password fails due to password format
// 'dialog.recording', // recording notifications (pending, on, off, limits)
// 'dialog.remoteControlTitle', // remote control notifications (allowed, denied, start, stop, error)
// 'dialog.reservationError',
// 'dialog.serviceUnavailable', // shown when server is not reachable
// 'dialog.sessTerminated', // shown when there is a failed conference session
// 'dialog.tokenAuthFailed', // show when an invalid jwt is used
// 'dialog.transcribing', // transcribing notifications (pending, off)
// 'dialOut.statusMessage', // shown when dial out status is updated.
// 'liveStreaming.busy', // shown when livestreaming service is busy
// 'liveStreaming.failedToStart', // shown when livestreaming fails to start
// 'liveStreaming.unavailableTitle', // shown when livestreaming service is not reachable
// 'lobby.joinRejectedMessage', // shown when while in a lobby, user's request to join is rejected
// 'lobby.notificationTitle', // shown when lobby is toggled and when join requests are allowed / denied
// 'localRecording.localRecording', // shown when a local recording is started
// 'notify.disconnected', // shown when a participant has left
// 'notify.grantedTo', // shown when moderator rights were granted to a participant
// 'notify.invitedOneMember', // shown when 1 participant has been invited
// 'notify.invitedThreePlusMembers', // shown when 3+ participants have been invited
// 'notify.invitedTwoMembers', // shown when 2 participants have been invited
// 'notify.kickParticipant', // shown when a participant is kicked
// 'notify.mutedRemotelyTitle', // shown when user is muted by a remote party
// 'notify.mutedTitle', // shown when user has been muted upon joining,
// 'notify.newDeviceAudioTitle', // prompts the user to use a newly detected audio device
// 'notify.newDeviceCameraTitle', // prompts the user to use a newly detected camera
// 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
// 'notify.passwordSetRemotely', // shown when a password has been set remotely
// 'notify.raisedHand', // shown when a partcipant used raise hand,
// 'notify.startSilentTitle', // shown when user joined with no audio
// 'prejoin.errorDialOut',
// 'prejoin.errorDialOutDisconnected',
// 'prejoin.errorDialOutFailed',
// 'prejoin.errorDialOutStatus',
// 'prejoin.errorStatusCode',
// 'prejoin.errorValidation',
// 'recording.busy', // shown when recording service is busy
// 'recording.failedToStart', // shown when recording fails to start
// 'recording.unavailableTitle', // shown when recording service is not reachable
// 'toolbar.noAudioSignalTitle', // shown when a broken mic is detected
// 'toolbar.noisyAudioInputTitle', // shown when noise is detected for the current microphone
// 'toolbar.talkWhileMutedPopup', // shown when user tries to speak while muted
// 'transcribing.failedToStart' // shown when transcribing fails to start
// ]
// Allow all above example options to include a trailing comma and
// prevent fear when commenting out the last value.

View File

@@ -14,12 +14,15 @@
text-overflow: ellipsis;
box-sizing: border-box;
white-space: nowrap;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
&.visible {
top: 0px;
}
&.gradient {
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
}
&-text {
vertical-align: middle;
}

View File

@@ -14,7 +14,8 @@ import {
} from '../../react/features/base/conference';
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
import JitsiMeetJS, { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
import { pinParticipant } from '../../react/features/base/participants';
import { pinParticipant, getParticipantById, kickParticipant } from '../../react/features/base/participants';
import { setPrivateMessageRecipient } from '../../react/features/chat/actions';
import {
processExternalDeviceRequest
} from '../../react/features/device-selection/functions';
@@ -330,6 +331,27 @@ function initCommands() {
} else {
logger.error('No recording or streaming session found');
}
},
'initiate-private-chat': participantId => {
const state = APP.store.getState();
const participant = getParticipantById(state, participantId);
if (participant) {
const { isOpen: isChatOpen } = state['features/chat'];
if (!isChatOpen) {
APP.UI.toggleChat();
}
APP.store.dispatch(setPrivateMessageRecipient(participant));
} else {
logger.error('No participant found for the given participantId');
}
},
'cancel-private-chat': () => {
APP.store.dispatch(setPrivateMessageRecipient());
},
'kick-participant': participantId => {
APP.store.dispatch(kickParticipant(participantId));
}
};
transport.on('event', ({ data, name }) => {
@@ -413,6 +435,15 @@ function initCommands() {
case 'is-sharing-screen':
callback(Boolean(APP.conference.isSharingScreen));
break;
case 'get-content-sharing-participants': {
const tracks = getState()['features/base/tracks'];
const sharingParticipantIds = tracks.filter(tr => tr.videoType === 'desktop').map(t => t.participantId);
callback({
sharingParticipantIds
});
break;
}
default:
return false;
}
@@ -564,8 +595,8 @@ class API {
* @returns {void}
*/
notifyReceivedChatMessage(
{ body, id, nick, ts }: {
body: *, id: string, nick: string, ts: *
{ body, id, nick, privateMessage, ts }: {
body: *, id: string, nick: string, privateMessage: boolean, ts: *
} = {}) {
if (APP.conference.isLocalId(id)) {
return;
@@ -576,6 +607,7 @@ class API {
from: id,
message: body,
nick,
privateMessage,
stamp: ts
});
}
@@ -656,6 +688,19 @@ class API {
});
}
/**
* Notify external application (if API is enabled) that the list of sharing participants changed.
*
* @param {Object} data - The event data.
* @returns {void}
*/
notifySharingParticipantsChanged(data: Object) {
this._sendEvent({
name: 'content-sharing-participants-changed',
data
});
}
/**
* Notify external application (if API is enabled) that the device list has
* changed.

View File

@@ -28,11 +28,14 @@ const ALWAYS_ON_TOP_FILENAMES = [
*/
const commands = {
avatarUrl: 'avatar-url',
cancelPrivateChat: 'cancel-private-chat',
displayName: 'display-name',
e2eeKey: 'e2ee-key',
email: 'email',
toggleLobby: 'toggle-lobby',
hangup: 'video-hangup',
intiatePrivateChat: 'initiate-private-chat',
kickParticipant: 'kick-participant',
muteEveryone: 'mute-everyone',
password: 'password',
pinParticipant: 'pin-participant',
@@ -62,6 +65,7 @@ const events = {
'audio-availability-changed': 'audioAvailabilityChanged',
'audio-mute-status-changed': 'audioMuteStatusChanged',
'camera-error': 'cameraError',
'content-sharing-participants-changed': 'contentSharingParticipantsChanged',
'device-list-changed': 'deviceListChanged',
'display-name-change': 'displayNameChange',
'email-change': 'emailChange',
@@ -723,6 +727,17 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
return getAvailableDevices(this._transport);
}
/**
* Gets a list of the currently sharing participant id's.
*
* @returns {Promise} - Resolves with the list of participant id's currently sharing.
*/
getContentSharingParticipants() {
return this._transport.sendRequest({
name: 'get-content-sharing-participants'
});
}
/**
* Returns Promise that resolves with current selected devices.
*

10
package-lock.json generated
View File

@@ -3325,9 +3325,9 @@
}
},
"@jitsi/js-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.3.tgz",
"integrity": "sha512-m6mZz7R716mHP21lTKQffyM0nNFu3Fe/EHCaOVLFY/vdPsaUl9DhypJqtPIYzRUfPnmnugdaxcxrUeSZQXQzVA==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.5.tgz",
"integrity": "sha512-1APQyuqQaYDR+W7cdgzsaBo6x8dpF8sfelcBf3ngNU3Jd+DzuuwUvCMTbr2+cCuy6w59ZAuQ7e2ixCnnOXOW4Q==",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3"
@@ -10816,8 +10816,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#87c6e374755718fdb0804c2c798ea4bc832f4fca",
"from": "github:jitsi/lib-jitsi-meet#87c6e374755718fdb0804c2c798ea4bc832f4fca",
"version": "github:jitsi/lib-jitsi-meet#76f265f04a5c44160493de82cd80b4e7d274836a",
"from": "github:jitsi/lib-jitsi-meet#76f265f04a5c44160493de82cd80b4e7d274836a",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "1.0.3",

View File

@@ -32,7 +32,7 @@
"@atlaskit/theme": "7.0.2",
"@atlaskit/toggle": "5.0.14",
"@atlaskit/tooltip": "12.1.13",
"@jitsi/js-utils": "1.0.3",
"@jitsi/js-utils": "1.0.5",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-async-storage/async-storage": "1.13.2",
"@react-native-community/google-signin": "3.0.1",
@@ -56,7 +56,7 @@
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#87c6e374755718fdb0804c2c798ea4bc832f4fca",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#76f265f04a5c44160493de82cd80b4e7d274836a",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.19",
"moment": "2.19.4",

View File

@@ -80,6 +80,7 @@ export default [
'disableAP',
'disableAudioLevels',
'disableDeepLinking',
'disableInitialGUM',
'disableH264',
'disableHPF',
'disableInviteFunctions',
@@ -131,6 +132,7 @@ export default [
'liveStreamingEnabled',
'localRecording',
'maxFullResolutionParticipants',
'notifications',
'openBridgeChannel',
'openSharedDocumentOnJoin',
'opusMaxAverageBitrate',

View File

@@ -2,6 +2,7 @@
import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
import { browser } from '../lib-jitsi-meet';
import { parseURLParams } from '../util/parseURLParams';
import logger from './logger';
@@ -23,7 +24,7 @@ function onFakeLocalStorageChanged() {
* @returns {void}
*/
function setupJitsiLocalStorage() {
if (jitsiLocalStorage.isLocalStorageDisabled()) {
if (jitsiLocalStorage.isLocalStorageDisabled() || browser.isSafari()) {
const urlParams = parseURLParams(window.location);
try {

View File

@@ -35,6 +35,8 @@ import {
setTrackMuted
} from './functions';
import './subscriber';
declare var APP: Object;
/**

View File

@@ -0,0 +1,24 @@
// @flow
import _ from 'lodash';
import { StateListenerRegistry } from '../../base/redux';
declare var APP: Object;
/**
* Notifies when the list of currently sharing participants changes.
*/
StateListenerRegistry.register(
/* selector */ state =>
state['features/base/tracks'].filter(tr => tr.videoType === 'desktop').map(t => t.participantId),
/* listener */ (participantIDs, store, previousParticipantIDs) => {
if (typeof APP !== 'object') {
return;
}
if (!_.isEqual(_.sortBy(participantIDs), _.sortBy(previousParticipantIDs))) {
APP.API.notifySharingParticipantsChanged(participantIDs);
}
}
);

View File

@@ -1,8 +1,3 @@
/**
* Action used to store the billing id.
*/
export const SET_BILLING_ID = 'SET_BILLING_ID';
/**
* Action used to store the flag signaling the endpoint has been counted.
*/

View File

@@ -1,13 +1,10 @@
// @flow
import uuid from 'uuid';
import { SET_BILLING_ID, SET_ENDPOINT_COUNTED } from './actionTypes';
import { SET_ENDPOINT_COUNTED } from './actionTypes';
import { extractVpaasTenantFromPath, getBillingId, sendCountRequest } from './functions';
/**
* Sends a billing count request when needed.
* If there is no billingId, it presists one first and sends the request after.
*
* @returns {Function}
*/
@@ -20,12 +17,7 @@ export function countEndpoint() {
const shouldSendRequest = Boolean(baseUrl && jwt && tenant);
if (shouldSendRequest) {
let billingId = getBillingId();
if (!billingId) {
billingId = uuid.v4();
dispatch(setBillingId(billingId));
}
const billingId = getBillingId();
sendCountRequest({
baseUrl,
@@ -38,19 +30,6 @@ export function countEndpoint() {
};
}
/**
* Action used to set the user billing id.
*
* @param {string} value - The uid.
* @returns {Object}
*/
function setBillingId(value) {
return {
type: SET_BILLING_ID,
value
};
}
/**
* Action used to mark the endpoint as counted.
*

View File

@@ -1,6 +1,7 @@
// @flow
import { jitsiLocalStorage } from '@jitsi/js-utils';
import uuid from 'uuid';
import { BILLING_ID, VPAAS_TENANT_PREFIX } from './constants';
import logger from './logger';
@@ -72,20 +73,18 @@ export async function sendCountRequest({ baseUrl, billingId, jwt, tenant }: {
}
/**
* Returns the stored billing id.
* Returns the stored billing id (or generates a new one if none is present).
*
* @returns {string}
*/
export function getBillingId() {
return jitsiLocalStorage.getItem(BILLING_ID);
}
/**
* Stores the billing id.
*
* @param {string} value - The id to be stored.
* @returns {void}
*/
export function setBillingId(value: string) {
jitsiLocalStorage.setItem(BILLING_ID, value);
let billingId = jitsiLocalStorage.getItem(BILLING_ID);
if (!billingId) {
billingId = uuid.v4();
jitsiLocalStorage.setItem(BILLING_ID, billingId);
}
return billingId;
}

View File

@@ -3,9 +3,8 @@ import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
import { PARTICIPANT_JOINED } from '../base/participants/actionTypes';
import { MiddlewareRegistry } from '../base/redux';
import { SET_BILLING_ID } from './actionTypes';
import { countEndpoint } from './actions';
import { isVpaasMeeting, extractVpaasTenantFromPath, setBillingId } from './functions';
import { isVpaasMeeting, extractVpaasTenantFromPath } from './functions';
/**
* The redux middleware for billing counter.
@@ -21,11 +20,6 @@ MiddlewareRegistry.register(store => next => async action => {
break;
}
case SET_BILLING_ID: {
setBillingId(action.value);
break;
}
case PARTICIPANT_JOINED: {
const shouldCount = !store.getState()['features/billing-counter'].endpointCounted

View File

@@ -19,7 +19,6 @@ import {
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { showToolbox } from '../toolbox/actions';
import { isButtonEnabled } from '../toolbox/functions';
import { SEND_MESSAGE, SET_PRIVATE_MESSAGE_RECIPIENT } from './actionTypes';
import { addMessage, clearMessages, toggleChat } from './actions';
@@ -152,10 +151,9 @@ StateListenerRegistry.register(
* @returns {void}
*/
function _addChatMsgListener(conference, store) {
if ((typeof APP !== 'undefined' && !isButtonEnabled('chat'))
|| store.getState()['features/base/config'].iAmRecorder) {
// We don't register anything on web if the chat button is not enabled in interfaceConfig
// or we are in iAmRecorder mode
if (store.getState()['features/base/config'].iAmRecorder) {
// We don't register anything on web if we are in iAmRecorder mode
return;
}
@@ -249,6 +247,7 @@ function _handleReceivedMessage({ dispatch, getState }, { id, message, privateMe
body: message,
id,
nick: displayName,
privateMessage,
ts: timestamp
});

View File

@@ -57,9 +57,14 @@ class Subject extends Component<Props> {
*/
render() {
const { _hideConferenceTimer, _showParticipantCount, _showSubject, _subject, _visible } = this.props;
let className = `subject ${_visible ? 'visible' : ''}`;
if (!_hideConferenceTimer || _showParticipantCount || _showSubject) {
className += ' gradient';
}
return (
<div className = { `subject ${_visible ? 'visible' : ''}` }>
<div className = { className }>
{ _showSubject && <span className = 'subject-text'>{ _subject }</span>}
{ _showParticipantCount && <ParticipantsCount /> }
{ !_hideConferenceTimer && <ConferenceTimer /> }

View File

@@ -4,6 +4,7 @@ import { i18next } from '../base/i18n';
import { isLocalParticipantModerator } from '../base/participants';
import { toState } from '../base/redux';
import { doGetJSON, parseURIString } from '../base/util';
import { isVpaasMeeting } from '../billing-counter/functions';
import logger from './logger';
@@ -352,7 +353,7 @@ export function invitePeopleAndChatRooms( // eslint-disable-line max-params
export function isAddPeopleEnabled(state: Object): boolean {
const { peopleSearchUrl } = state['features/base/config'];
return state['features/base/jwt'].jwt && Boolean(peopleSearchUrl);
return state['features/base/jwt'].jwt && Boolean(peopleSearchUrl) && !isVpaasMeeting(state);
}
/**

View File

@@ -46,14 +46,14 @@ MiddlewareRegistry.register(({ getState, dispatch }) => next => action => {
recordingController.onWarning = (messageKey, messageParams) => {
dispatch(showNotification({
title: i18next.t('localRecording.localRecording'),
titleKey: 'localRecording.localRecording',
description: i18next.t(messageKey, messageParams)
}, 10000));
};
recordingController.onNotify = (messageKey, messageParams) => {
dispatch(showNotification({
title: i18next.t('localRecording.localRecording'),
titleKey: 'localRecording.localRecording',
description: i18next.t(messageKey, messageParams)
}, 10000));
};

View File

@@ -108,20 +108,20 @@ async function _handleNoAudioSignalNotification({ dispatch, getState }, action)
};
}
const notification = showNotification({
const notification = await dispatch(showNotification({
titleKey: 'toolbar.noAudioSignalTitle',
description: <DialInLink />,
descriptionKey,
customActionNameKey,
customActionHandler
});
dispatch(notification);
}));
dispatch(playSound(NO_AUDIO_SIGNAL_SOUND_ID));
// Store the current notification uid so we can check for this state and hide it in case
// a new track was added, thus changing the context of the notification
dispatch(setNoAudioSignalNotificationUid(notification.uid));
if (notification) {
// Store the current notification uid so we can check for this state and hide it in case
// a new track was added, thus changing the context of the notification
dispatch(setNoAudioSignalNotificationUid(notification.uid));
}
});
}

View File

@@ -37,17 +37,18 @@ MiddlewareRegistry.register(store => next => action => {
}
});
conference.on(
JitsiConferenceEvents.NOISY_MIC, () => {
const notification = showNotification({
JitsiConferenceEvents.NOISY_MIC, async () => {
const notification = await dispatch(showNotification({
titleKey: 'toolbar.noisyAudioInputTitle',
descriptionKey: 'toolbar.noisyAudioInputDesc'
});
}));
dispatch(notification);
dispatch(playSound(NOISY_AUDIO_INPUT_SOUND_ID));
// we store the last notification id so we can hide it if the mic is muted
dispatch(setNoisyAudioInputNotificationUid(notification.uid));
if (notification) {
// we store the last notification id so we can hide it if the mic is muted
dispatch(setNoisyAudioInputNotificationUid(notification.uid));
}
});
break;
}

View File

@@ -76,19 +76,23 @@ export function showErrorNotification(props: Object) {
* @param {Object} props - The props needed to show the notification component.
* @param {number} timeout - How long the notification should display before
* automatically being hidden.
* @returns {{
* type: SHOW_NOTIFICATION,
* props: Object,
* timeout: number,
* uid: number
* }}
* @returns {Function}
*/
export function showNotification(props: Object = {}, timeout: ?number) {
return {
type: SHOW_NOTIFICATION,
props,
timeout,
uid: window.Date.now()
return function(dispatch: Function, getState: Function) {
const { notifications } = getState()['features/base/config'];
const shouldDisplay = !notifications
|| notifications.includes(props.descriptionKey)
|| notifications.includes(props.titleKey);
if (shouldDisplay) {
return dispatch({
type: SHOW_NOTIFICATION,
props,
timeout,
uid: window.Date.now()
});
}
};
}

View File

@@ -75,7 +75,7 @@ export function setLiveStreamKey(streamKey: string) {
* @returns {Function}
*/
export function showPendingRecordingNotification(streamType: string) {
return (dispatch: Function) => {
return async (dispatch: Function) => {
const isLiveStreaming
= streamType === JitsiMeetJS.constants.recording.mode.STREAM;
const dialogProps = isLiveStreaming ? {
@@ -85,15 +85,14 @@ export function showPendingRecordingNotification(streamType: string) {
descriptionKey: 'recording.pending',
titleKey: 'dialog.recording'
};
const showNotificationAction = showNotification({
const notification = await dispatch(showNotification({
isDismissAllowed: false,
...dialogProps
});
}));
dispatch(showNotificationAction);
dispatch(_setPendingRecordingNotificationUid(
showNotificationAction.uid, streamType));
if (notification) {
dispatch(_setPendingRecordingNotificationUid(notification.uid, streamType));
}
};
}

View File

@@ -40,20 +40,20 @@ MiddlewareRegistry.register(store => next => action => {
}
});
conference.on(
JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
const notification = showNotification({
JitsiConferenceEvents.TALK_WHILE_MUTED, async () => {
const notification = await dispatch(showNotification({
titleKey: 'toolbar.talkWhileMutedPopup',
customActionNameKey: 'notify.unmute',
customActionHandler: () => dispatch(setAudioMuted(false))
});
dispatch(notification);
}));
dispatch(playSound(TALK_WHILE_MUTED_SOUND_ID));
// we store the last start muted notification id that we showed,
// so we can hide it when unmuted mic is detected
dispatch(setCurrentNotificationUid(notification.uid));
if (notification) {
// we store the last start muted notification id that we showed,
// so we can hide it when unmuted mic is detected
dispatch(setCurrentNotificationUid(notification.uid));
}
});
break;
}

View File

@@ -69,17 +69,16 @@ export function potentialTranscriberJoined(participantId: string) {
* @returns {Function}
*/
export function showPendingTranscribingNotification() {
return (dispatch: Function) => {
const showNotificationAction = showNotification({
return async (dispatch: Function) => {
const notification = await dispatch(showNotification({
descriptionKey: 'transcribing.pending',
isDismissAllowed: false,
titleKey: 'dialog.transcribing'
});
}));
dispatch(showNotificationAction);
dispatch(setPendingTranscribingNotificationUid(
showNotificationAction.uid));
if (notification) {
dispatch(setPendingTranscribingNotificationUid(notification.uid));
}
};
}