mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-03-24 04:20:20 +00:00
Compare commits
33 Commits
android-ap
...
7260
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a22db037c7 | ||
|
|
44cc0f7e9a | ||
|
|
eafb337cd1 | ||
|
|
9b3be66287 | ||
|
|
1a10a00f74 | ||
|
|
a196bc27b8 | ||
|
|
ac8e4d9828 | ||
|
|
a6ade336b7 | ||
|
|
350443ad34 | ||
|
|
4c37ef7a2c | ||
|
|
3eedc2a49d | ||
|
|
aaeb1a90e5 | ||
|
|
ed89f9af20 | ||
|
|
863ad0b0e6 | ||
|
|
e2d701a8cc | ||
|
|
2710273069 | ||
|
|
07af18e284 | ||
|
|
519e37f567 | ||
|
|
7cf61eb776 | ||
|
|
f81446909c | ||
|
|
b4115593c0 | ||
|
|
f8bd8b616e | ||
|
|
be55ccd6f4 | ||
|
|
e7db18bd80 | ||
|
|
dff41e0fcb | ||
|
|
43be4324af | ||
|
|
c33baf4c96 | ||
|
|
f95a356025 | ||
|
|
1ba7765898 | ||
|
|
0346fca434 | ||
|
|
d267b2499d | ||
|
|
e3e5f1fbfa | ||
|
|
4697192b43 |
@@ -19,7 +19,7 @@ buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
compileSdkVersion = 32
|
||||
minSdkVersion = 23
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 32
|
||||
supportLibVersion = "28.0.0"
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ class AudioDeviceHandlerGeneric implements
|
||||
*/
|
||||
private AudioModeModule module;
|
||||
|
||||
/**
|
||||
* Constant defining a Hearing Aid. Only available on API level >= 28.
|
||||
* The value of: AudioDeviceInfo.TYPE_HEARING_AID
|
||||
*/
|
||||
private static final int TYPE_HEARING_AID = 23;
|
||||
|
||||
/**
|
||||
* Constant defining a USB headset. Only available on API level >= 26.
|
||||
* The value of: AudioDeviceInfo.TYPE_USB_HEADSET
|
||||
@@ -85,6 +91,7 @@ class AudioDeviceHandlerGeneric implements
|
||||
break;
|
||||
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
|
||||
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
|
||||
case TYPE_HEARING_AID:
|
||||
case TYPE_USB_HEADSET:
|
||||
devices.add(AudioModeModule.DEVICE_HEADPHONES);
|
||||
break;
|
||||
|
||||
183
conference.js
183
conference.js
@@ -559,7 +559,7 @@ export default {
|
||||
);
|
||||
}
|
||||
|
||||
let tryCreateLocalTracks;
|
||||
let tryCreateLocalTracks = Promise.resolve([]);
|
||||
|
||||
// On Electron there is no permission prompt for granting permissions. That's why we don't need to
|
||||
// spend much time displaying the overlay screen. If GUM is not resolved within 15 seconds it will
|
||||
@@ -600,76 +600,65 @@ export default {
|
||||
|
||||
return [];
|
||||
});
|
||||
} else if (!requestedAudio && !requestedVideo) {
|
||||
// Resolve with no tracks
|
||||
tryCreateLocalTracks = Promise.resolve([]);
|
||||
} else {
|
||||
} else if (requestedAudio || requestedVideo) {
|
||||
tryCreateLocalTracks = createLocalTracksF({
|
||||
devices: initialDevices,
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true
|
||||
})
|
||||
.catch(err => {
|
||||
if (requestedAudio && requestedVideo) {
|
||||
|
||||
// Try audio only...
|
||||
errors.audioAndVideoError = err;
|
||||
|
||||
if (err.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
|
||||
// In this case we expect that the permission prompt is still visible. There is no point of
|
||||
// executing GUM with different source. Also at the time of writing the following
|
||||
// inconsistency have been noticed in some browsers - if the permissions prompt is visible
|
||||
// and another GUM is executed the prompt does not change its content but if the user
|
||||
// clicks allow the user action isassociated with the latest GUM call.
|
||||
errors.audioOnlyError = err;
|
||||
errors.videoOnlyError = err;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return createLocalTracksF(audioOptions);
|
||||
} else if (requestedAudio && !requestedVideo) {
|
||||
errors.audioOnlyError = err;
|
||||
|
||||
return [];
|
||||
} else if (requestedVideo && !requestedAudio) {
|
||||
errors.videoOnlyError = err;
|
||||
|
||||
return [];
|
||||
}
|
||||
logger.error('Should never happen');
|
||||
})
|
||||
.catch(err => {
|
||||
// Log this just in case...
|
||||
if (!requestedAudio) {
|
||||
logger.error('The impossible just happened', err);
|
||||
}
|
||||
errors.audioOnlyError = err;
|
||||
|
||||
// Try video only...
|
||||
return requestedVideo
|
||||
? createLocalTracksF({
|
||||
devices: [ MEDIA_TYPE.VIDEO ],
|
||||
firePermissionPromptIsShownEvent: true
|
||||
})
|
||||
: [];
|
||||
})
|
||||
.catch(err => {
|
||||
// Log this just in case...
|
||||
if (!requestedVideo) {
|
||||
logger.error('The impossible just happened', err);
|
||||
}
|
||||
errors.videoOnlyError = err;
|
||||
.catch(async error => {
|
||||
if (error.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
|
||||
errors.audioAndVideoError = error;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// Retry with separate gUM calls.
|
||||
const gUMPromises = [];
|
||||
const tracks = [];
|
||||
|
||||
if (requestedAudio) {
|
||||
gUMPromises.push(createLocalTracksF(audioOptions));
|
||||
}
|
||||
|
||||
if (requestedVideo) {
|
||||
gUMPromises.push(createLocalTracksF({
|
||||
devices: [ MEDIA_TYPE.VIDEO ],
|
||||
timeout,
|
||||
firePermissionPromptIsShownEvent: true
|
||||
}));
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(gUMPromises);
|
||||
let errorMsg;
|
||||
|
||||
results.forEach((result, idx) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
tracks.push(result.value[0]);
|
||||
} else {
|
||||
errorMsg = result.reason;
|
||||
const isAudio = idx === 0;
|
||||
|
||||
logger.error(`${isAudio ? 'Audio' : 'Video'} track creation failed with error ${errorMsg}`);
|
||||
if (isAudio) {
|
||||
errors.audioOnlyError = errorMsg;
|
||||
} else {
|
||||
errors.videoOnlyError = errorMsg;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (errors.audioOnlyError && errors.videoOnlyError) {
|
||||
errors.audioAndVideoError = errorMsg;
|
||||
}
|
||||
|
||||
return tracks;
|
||||
});
|
||||
}
|
||||
|
||||
// Hide the permissions prompt/overlay as soon as the tracks are
|
||||
// created. Don't wait for the connection to be made, since in some
|
||||
// cases, when auth is required, for instance, that won't happen until
|
||||
// the user inputs their credentials, but the dialog would be
|
||||
// overshadowed by the overlay.
|
||||
// Hide the permissions prompt/overlay as soon as the tracks are created. Don't wait for the connection to
|
||||
// be established, as in some cases like when auth is required, connection won't be established until the user
|
||||
// inputs their credentials, but the dialog would be overshadowed by the overlay.
|
||||
tryCreateLocalTracks.then(tracks => {
|
||||
APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false));
|
||||
|
||||
@@ -810,43 +799,51 @@ export default {
|
||||
const initialOptions = {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: getStartWithAudioMuted(state)
|
||||
|| isUserInteractionRequiredForUnmute(state),
|
||||
startWithVideoMuted: getStartWithVideoMuted(state)
|
||||
|| isUserInteractionRequiredForUnmute(state)
|
||||
startWithAudioMuted: getStartWithAudioMuted(state) || isUserInteractionRequiredForUnmute(state),
|
||||
startWithVideoMuted: getStartWithVideoMuted(state) || isUserInteractionRequiredForUnmute(state)
|
||||
};
|
||||
|
||||
this.roomName = roomName;
|
||||
|
||||
try {
|
||||
// Initialize the device list first. This way, when creating tracks
|
||||
// based on preferred devices, loose label matching can be done in
|
||||
// cases where the exact ID match is no longer available, such as
|
||||
// when the camera device has switched USB ports.
|
||||
// when in startSilent mode we want to start with audio muted
|
||||
// Initialize the device list first. This way, when creating tracks based on preferred devices, loose label
|
||||
// matching can be done in cases where the exact ID match is no longer available, such as -
|
||||
// 1. When the camera device has switched USB ports.
|
||||
// 2. When in startSilent mode we want to start with audio muted
|
||||
await this._initDeviceList();
|
||||
} catch (error) {
|
||||
logger.warn('initial device list initialization failed', error);
|
||||
}
|
||||
|
||||
const handleStartAudioMuted = (options, tracks) => {
|
||||
if (options.startWithAudioMuted) {
|
||||
// Filter out the local tracks based on various config options, i.e., when user joins muted or is muted by
|
||||
// focus. However, audio track will always be created even though it is not added to the conference since we
|
||||
// want audio related features (noisy mic, talk while muted, etc.) to work even if the mic is muted.
|
||||
const handleInitialTracks = (options, tracks) => {
|
||||
let localTracks = tracks;
|
||||
|
||||
// No local tracks are added when user joins as a visitor.
|
||||
if (iAmVisitor(state)) {
|
||||
return [];
|
||||
}
|
||||
if (options.startWithAudioMuted || room?.isStartAudioMuted()) {
|
||||
// Always add the track on Safari because of a known issue where audio playout doesn't happen
|
||||
// if the user joins audio and video muted, i.e., if there is no local media capture.
|
||||
if (browser.isWebKitBased()) {
|
||||
this.muteAudio(true, true);
|
||||
} else {
|
||||
return tracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
if (room?.isStartVideoMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
return tracks;
|
||||
return localTracks;
|
||||
};
|
||||
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
_connectionPromise = connect(roomName).then(c => {
|
||||
// we want to initialize it early, in case of errors to be able
|
||||
// to gather logs
|
||||
// We want to initialize it early, in case of errors to be able to gather logs.
|
||||
APP.connection = c;
|
||||
|
||||
return c;
|
||||
@@ -859,48 +856,28 @@ export default {
|
||||
APP.store.dispatch(makePrecallTest(this._getConferenceOptions()));
|
||||
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
|
||||
const tracks = await tryCreateLocalTracks;
|
||||
const localTracks = await tryCreateLocalTracks;
|
||||
|
||||
// Initialize device list a second time to ensure device labels
|
||||
// get populated in case of an initial gUM acceptance; otherwise
|
||||
// they may remain as empty strings.
|
||||
// Initialize device list a second time to ensure device labels get populated in case of an initial gUM
|
||||
// acceptance; otherwise they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
return APP.store.dispatch(initPrejoin(tracks, errors));
|
||||
return APP.store.dispatch(initPrejoin(localTracks, errors));
|
||||
}
|
||||
|
||||
logger.debug('Prejoin screen no longer displayed at the time when tracks were created');
|
||||
|
||||
this._displayErrorsForCreateInitialLocalTracks(errors);
|
||||
|
||||
let localTracks = handleStartAudioMuted(initialOptions, tracks);
|
||||
|
||||
// In case where gUM is slow and resolves after the startAudio/VideoMuted coming from jicofo, we can be
|
||||
// join unmuted even though jicofo had instruct us to mute, so let's respect that before passing the tracks
|
||||
if (!browser.isWebKitBased()) {
|
||||
if (room?.isStartAudioMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
if (room?.isStartVideoMuted()) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.VIDEO);
|
||||
}
|
||||
|
||||
// Do not add the tracks if the user has joined the call as a visitor.
|
||||
if (iAmVisitor(state)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._setLocalAudioVideoStreams(localTracks);
|
||||
return this._setLocalAudioVideoStreams(handleInitialTracks(initialOptions, localTracks));
|
||||
}
|
||||
|
||||
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(roomName, initialOptions);
|
||||
|
||||
this._initDeviceList(true);
|
||||
|
||||
return this.startConference(con, handleStartAudioMuted(initialOptions, tracks));
|
||||
return this.startConference(con, handleInitialTracks(initialOptions, tracks));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1073,7 +1050,7 @@ export default {
|
||||
*/
|
||||
muteVideo(mute, showUI = true) {
|
||||
if (this.videoSwitchInProgress) {
|
||||
console.warn('muteVideo - unable to perform operations while video switch is in progress');
|
||||
logger.warn('muteVideo - unable to perform operations while video switch is in progress');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,10 +74,6 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ PODS:
|
||||
- AppAuth/Core (~> 1.6)
|
||||
- GTMSessionFetcher/Core (< 3.0, >= 1.5)
|
||||
- GTMSessionFetcher/Core (2.3.0)
|
||||
- JitsiWebRTC (111.0.1)
|
||||
- JitsiWebRTC (111.0.2)
|
||||
- libwebp (1.2.4):
|
||||
- libwebp/demux (= 1.2.4)
|
||||
- libwebp/mux (= 1.2.4)
|
||||
@@ -725,7 +725,7 @@ SPEC CHECKSUMS:
|
||||
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
|
||||
GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd
|
||||
GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2
|
||||
JitsiWebRTC: 9619c1f71cc16eeca76df68aa2d213c6d63274a8
|
||||
JitsiWebRTC: 80f62908fcf2a1160e0d14b584323fb6e6be630b
|
||||
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
|
||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
||||
ObjectiveDropboxOfficial: fe206ce8c0bc49976c249d472db7fdbc53ebbd53
|
||||
|
||||
@@ -98,7 +98,6 @@ platform :ios do
|
||||
demo_account_required: false,
|
||||
distribute_external: true,
|
||||
groups: ENV["JITSI_BETA_TESTING_GROUPS"],
|
||||
reject_build_waiting_for_review: true,
|
||||
uses_non_exempt_encryption: false
|
||||
)
|
||||
|
||||
|
||||
@@ -1150,6 +1150,7 @@
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
"raiseHand": "Raise your hand",
|
||||
"reactions": "Reactions",
|
||||
"reactionsMenu": "Reactions menu",
|
||||
"recording": "Toggle recording",
|
||||
"remoteMute": "Mute participant",
|
||||
@@ -1247,6 +1248,7 @@
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"reactions": "Reactions",
|
||||
"security": "Security options",
|
||||
"selectBackground": "Select background",
|
||||
"shareRoom": "Invite someone",
|
||||
|
||||
@@ -113,6 +113,8 @@ import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/function
|
||||
import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any';
|
||||
import { muteAllParticipants } from '../../react/features/video-menu/actions';
|
||||
import { setVideoQuality } from '../../react/features/video-quality/actions';
|
||||
import { toggleWhiteboard } from '../../react/features/whiteboard/actions.any';
|
||||
import { WhiteboardStatus } from '../../react/features/whiteboard/types';
|
||||
import { getJitsiMeetTransport } from '../transport';
|
||||
|
||||
import {
|
||||
@@ -833,6 +835,9 @@ function initCommands() {
|
||||
} else {
|
||||
logger.error(' End Conference not supported');
|
||||
}
|
||||
},
|
||||
'toggle-whiteboard': () => {
|
||||
APP.store.dispatch(toggleWhiteboard());
|
||||
}
|
||||
};
|
||||
transport.on('event', ({ data, name }) => {
|
||||
@@ -2014,6 +2019,20 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) if whiteboard state is
|
||||
* changed.
|
||||
*
|
||||
* @param {WhiteboardStatus} status - The new whiteboard status.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyWhiteboardStatusChanged(status: WhiteboardStatus) {
|
||||
this._sendEvent({
|
||||
name: 'whiteboard-status-changed',
|
||||
status
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the allocated resources.
|
||||
*
|
||||
|
||||
6
modules/API/external/external_api.js
vendored
6
modules/API/external/external_api.js
vendored
@@ -90,7 +90,8 @@ const commands = {
|
||||
toggleSubtitles: 'toggle-subtitles',
|
||||
toggleTileView: 'toggle-tile-view',
|
||||
toggleVirtualBackgroundDialog: 'toggle-virtual-background',
|
||||
toggleVideo: 'toggle-video'
|
||||
toggleVideo: 'toggle-video',
|
||||
toggleWhiteboard: 'toggle-whiteboard'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -154,7 +155,8 @@ const events = {
|
||||
'subject-change': 'subjectChange',
|
||||
'suspend-detected': 'suspendDetected',
|
||||
'tile-view-changed': 'tileViewChanged',
|
||||
'toolbar-button-clicked': 'toolbarButtonClicked'
|
||||
'toolbar-button-clicked': 'toolbarButtonClicked',
|
||||
'whiteboard-status-changed': 'whiteboardStatusChanged'
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
153
package-lock.json
generated
153
package-lock.json
generated
@@ -60,7 +60,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1631.0.0+f0dd4039/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -71,7 +71,7 @@
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-emoji-render": "1.2.4",
|
||||
"react-focus-lock": "2.5.1",
|
||||
"react-focus-lock": "2.9.4",
|
||||
"react-i18next": "10.11.4",
|
||||
"react-linkify": "1.0.0-alpha",
|
||||
"react-native": "0.68.6",
|
||||
@@ -10508,9 +10508,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/focus-lock": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz",
|
||||
"integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==",
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.6.tgz",
|
||||
"integrity": "sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
@@ -12739,8 +12739,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1631.0.0+f0dd4039/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-as7vkc9NvpXhNC2vkvrRnxZiPs9Uc4PXbphBXlh5h8LTNtvnMEPWH9FSsBzKukU/2ITIPOhCEJqhCIurkZfPKg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -15415,14 +15415,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-clientside-effect": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz",
|
||||
"integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
|
||||
"integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.3.0 || ^16.0.0 || ^17.0.0"
|
||||
"react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-devtools-core": {
|
||||
@@ -15484,19 +15484,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-focus-lock": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.5.1.tgz",
|
||||
"integrity": "sha512-gOToRZKVEymGEjFaTRUKgJsdYQrNosoiK7yZnXnnd8bYew4vMzk3Rxb0Q4nyrGwsFuUmgQiSAulQirA0J+v4hA==",
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.4.tgz",
|
||||
"integrity": "sha512-7pEdXyMseqm3kVjhdVH18sovparAzLg5h6WvIx7/Ck3ekjhrrDMEegHSa3swwC8wgfdd7DIdUVRGeiHT9/7Sgg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"focus-lock": "^0.9.1",
|
||||
"focus-lock": "^0.11.6",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-clientside-effect": "^1.2.2",
|
||||
"use-callback-ref": "^1.2.1",
|
||||
"use-sidecar": "^1.0.1"
|
||||
"react-clientside-effect": "^1.2.6",
|
||||
"use-callback-ref": "^1.3.0",
|
||||
"use-sidecar": "^1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-freeze": {
|
||||
@@ -17627,15 +17633,6 @@
|
||||
"ws": "^8.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strophe.js/node_modules/ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
@@ -18572,15 +18569,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/use-callback-ref": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
|
||||
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
|
||||
"integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.5.0"
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
@@ -18639,25 +18639,26 @@
|
||||
"integrity": "sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ=="
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz",
|
||||
"integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^1.9.3"
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.5.0"
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
"@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/use-subscription": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz",
|
||||
@@ -27450,9 +27451,9 @@
|
||||
"integrity": "sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg=="
|
||||
},
|
||||
"focus-lock": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz",
|
||||
"integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==",
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.6.tgz",
|
||||
"integrity": "sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
@@ -29117,8 +29118,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0LK5EMCvtsAEQkjkrWzyniTl8BKtshAZxI3e9hGnA/RVDuULLXre7GEWd2zCP3tCfdxclkhqt7skQpfNhCTdqg==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1631.0.0+f0dd4039/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-as7vkc9NvpXhNC2vkvrRnxZiPs9Uc4PXbphBXlh5h8LTNtvnMEPWH9FSsBzKukU/2ITIPOhCEJqhCIurkZfPKg==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.0.0",
|
||||
"@jitsi/logger": "2.0.0",
|
||||
@@ -31170,9 +31171,9 @@
|
||||
}
|
||||
},
|
||||
"react-clientside-effect": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.5.tgz",
|
||||
"integrity": "sha512-2bL8qFW1TGBHozGGbVeyvnggRpMjibeZM2536AKNENLECutp2yfs44IL8Hmpn8qjFQ2K7A9PnYf3vc7aQq/cPA==",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
|
||||
"integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.13"
|
||||
}
|
||||
@@ -31216,16 +31217,16 @@
|
||||
}
|
||||
},
|
||||
"react-focus-lock": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.5.1.tgz",
|
||||
"integrity": "sha512-gOToRZKVEymGEjFaTRUKgJsdYQrNosoiK7yZnXnnd8bYew4vMzk3Rxb0Q4nyrGwsFuUmgQiSAulQirA0J+v4hA==",
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.4.tgz",
|
||||
"integrity": "sha512-7pEdXyMseqm3kVjhdVH18sovparAzLg5h6WvIx7/Ck3ekjhrrDMEegHSa3swwC8wgfdd7DIdUVRGeiHT9/7Sgg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"focus-lock": "^0.9.1",
|
||||
"focus-lock": "^0.11.6",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-clientside-effect": "^1.2.2",
|
||||
"use-callback-ref": "^1.2.1",
|
||||
"use-sidecar": "^1.0.1"
|
||||
"react-clientside-effect": "^1.2.6",
|
||||
"use-callback-ref": "^1.3.0",
|
||||
"use-sidecar": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"react-freeze": {
|
||||
@@ -32849,18 +32850,12 @@
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
|
||||
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
|
||||
"requires": {
|
||||
"@xmldom/xmldom": "0.8.2",
|
||||
"@xmldom/xmldom": "0.8.7",
|
||||
"abab": "^2.0.3",
|
||||
"karma-rollup-preprocessor": "^7.0.8",
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"optional": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
@@ -33536,9 +33531,12 @@
|
||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
|
||||
},
|
||||
"use-callback-ref": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
|
||||
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg=="
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz",
|
||||
"integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"use-composed-ref": {
|
||||
"version": "1.2.1",
|
||||
@@ -33569,19 +33567,12 @@
|
||||
"integrity": "sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ=="
|
||||
},
|
||||
"use-sidecar": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.5.tgz",
|
||||
"integrity": "sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
|
||||
"requires": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"use-subscription": {
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1626.0.0+a41aa571/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1631.0.0+f0dd4039/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -76,7 +76,7 @@
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-emoji-render": "1.2.4",
|
||||
"react-focus-lock": "2.5.1",
|
||||
"react-focus-lock": "2.9.4",
|
||||
"react-i18next": "10.11.4",
|
||||
"react-linkify": "1.0.0-alpha",
|
||||
"react-native": "0.68.6",
|
||||
@@ -179,7 +179,7 @@
|
||||
"webpack-dev-server": "4.7.3"
|
||||
},
|
||||
"overrides": {
|
||||
"strophe.js@1.6.0": {
|
||||
"strophe.js@1.5.0": {
|
||||
"@xmldom/xmldom": "0.8.7"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import { setRoom } from '../base/conference/actions';
|
||||
import {
|
||||
configWillLoad,
|
||||
@@ -20,14 +19,11 @@ import {
|
||||
parseURIString,
|
||||
toURLString
|
||||
} from '../base/util/uri';
|
||||
// @ts-ignore
|
||||
import { isPrejoinPageEnabled } from '../mobile/navigation/functions';
|
||||
import {
|
||||
goBackToRoot,
|
||||
navigateRoot
|
||||
// @ts-ignore
|
||||
} from '../mobile/navigation/rootNavigationContainerRef';
|
||||
// @ts-ignore
|
||||
import { screen } from '../mobile/navigation/routes';
|
||||
import { clearNotifications } from '../notifications/actions';
|
||||
|
||||
@@ -143,7 +139,6 @@ export function appNavigate(uri?: string, options: IReloadNowOptions = {}) {
|
||||
dispatch(createDesiredLocalTracks());
|
||||
dispatch(clearNotifications());
|
||||
|
||||
// @ts-ignore
|
||||
const { hidePrejoin } = options;
|
||||
|
||||
if (!hidePrejoin && isPrejoinPageEnabled(getState())) {
|
||||
|
||||
@@ -5,8 +5,6 @@ import { IStateful } from '../base/app/types';
|
||||
import { isRoomValid } from '../base/conference/functions';
|
||||
import { isSupportedBrowser } from '../base/environment/environment';
|
||||
import { toState } from '../base/redux/functions';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import Conference from '../conference/components/web/Conference';
|
||||
import { getDeepLinkingPage } from '../deep-linking/functions';
|
||||
import UnsupportedDesktopBrowser from '../unsupported-browser/components/UnsupportedDesktopBrowser';
|
||||
|
||||
@@ -27,13 +27,13 @@ export function cancelLogin() {
|
||||
// a reaction to CONNECTION_FAILED). Since the
|
||||
// app/user is going to navigate to WelcomePage, the SDK
|
||||
// clients/consumers need an event.
|
||||
const { error, passwordRequired }
|
||||
const { error = { recoverable: undefined }, passwordRequired }
|
||||
= getState()['features/base/connection'];
|
||||
|
||||
passwordRequired
|
||||
&& dispatch(
|
||||
connectionFailed(
|
||||
passwordRequired, // @ts-ignore
|
||||
passwordRequired,
|
||||
set(error, 'recoverable', false) as any));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
export { default as LoginDialog } from './native/LoginDialog';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
export { default as WaitForOwnerDialog } from './native/WaitForOwnerDialog';
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
openLoginDialog,
|
||||
openWaitForOwnerDialog,
|
||||
stopWaitForOwner,
|
||||
waitForOwner } from './actions.native'; // @ts-ignore
|
||||
waitForOwner } from './actions.native';
|
||||
import { LoginDialog, WaitForOwnerDialog } from './components';
|
||||
|
||||
/**
|
||||
|
||||
@@ -300,7 +300,8 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
|
||||
if (!vnode) {
|
||||
// this is redirecting back to main, lets restore config
|
||||
// no point of updating disableFocus, we can skip the initial iq to jicofo
|
||||
// not updating disableFocus, as if the room capacity is full the promotion to the main room will fail
|
||||
// and the visitor will be redirected back to a vnode from jicofo
|
||||
if (config.oldConfig && username) {
|
||||
return {
|
||||
hosts: {
|
||||
@@ -310,6 +311,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
focusUserJid: focusJid,
|
||||
disableLocalStats: false,
|
||||
bosh: config.oldConfig.bosh && appendURLParam(config.oldConfig.bosh, 'customusername', username),
|
||||
p2p: config.oldConfig.p2p,
|
||||
websocket: config.oldConfig.websocket
|
||||
&& appendURLParam(config.oldConfig.websocket, 'customusername', username),
|
||||
oldConfig: undefined // clears it up
|
||||
@@ -326,6 +328,7 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
},
|
||||
focusUserJid: config.focusUserJid,
|
||||
bosh: config.bosh,
|
||||
p2p: config.p2p,
|
||||
websocket: config.websocket
|
||||
};
|
||||
|
||||
@@ -341,6 +344,10 @@ export function getVisitorOptions(stateful: IStateful, params: Array<string>) {
|
||||
disableFocus: true, // This flag disables sending the initial conference request
|
||||
disableLocalStats: true,
|
||||
bosh: config.bosh && appendURLParam(config.bosh, 'vnode', vnode),
|
||||
p2p: {
|
||||
...config.p2p,
|
||||
enabled: false
|
||||
},
|
||||
websocket: config.websocket && appendURLParam(config.websocket, 'vnode', vnode)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -289,6 +289,12 @@ function _conferenceJoined({ dispatch, getState }: IStore, next: Function, actio
|
||||
dispatch(conferenceWillLeave(conference));
|
||||
};
|
||||
|
||||
if (!iAmVisitor(getState())) {
|
||||
// if a visitor is promoted back to main room and want to join an empty breakout room
|
||||
// we need to send iq to jicofo, so it can join/create the breakout room
|
||||
dispatch(overwriteConfig({ disableFocus: false }));
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
window.addEventListener(disableBeforeUnloadHandlers ? 'unload' : 'beforeunload', beforeUnloadHandler);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
|
||||
@@ -18,6 +18,7 @@ type ToolbarButtons = 'camera' |
|
||||
'participants-pane' |
|
||||
'profile' |
|
||||
'raisehand' |
|
||||
'reactions' |
|
||||
'recording' |
|
||||
'security' |
|
||||
'select-background' |
|
||||
|
||||
@@ -64,7 +64,7 @@ export const THIRD_PARTY_PREJOIN_BUTTONS = [ 'microphone', 'camera', 'select-bac
|
||||
/**
|
||||
* The toolbar buttons to show when in visitors mode.
|
||||
*/
|
||||
export const VISITORS_MODE_BUTTONS = [ 'chat', 'hangup', 'raisehand', 'tileview' ];
|
||||
export const VISITORS_MODE_BUTTONS = [ 'chat', 'hangup', 'raisehand', 'settings', 'tileview' ];
|
||||
|
||||
/**
|
||||
* The set of feature flags.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Bourne from '@hapi/bourne';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ export interface IConfigState extends IConfig {
|
||||
domain: string;
|
||||
muc: string;
|
||||
};
|
||||
p2p?: object;
|
||||
websocket?: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ type Props = {
|
||||
/**
|
||||
* Function to render a bottom sheet footer element, if necessary.
|
||||
*/
|
||||
renderFooter?: Function;
|
||||
renderFooter?: () => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Function to render a bottom sheet header element, if necessary.
|
||||
@@ -109,9 +109,7 @@ class BottomSheet extends PureComponent<Props> {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<SlidingView // @ts-ignore
|
||||
accessibilityRole = 'menu'
|
||||
accessibilityViewIsModal = { true }
|
||||
<SlidingView
|
||||
onHide = { this._onCancel }
|
||||
position = 'bottom'
|
||||
show = { Boolean(showSlidingView) }>
|
||||
|
||||
@@ -18,7 +18,7 @@ interface IProps extends AbstractProps, WithTranslation {
|
||||
/**
|
||||
* The dialog descriptionKey.
|
||||
*/
|
||||
descriptionKey: string;
|
||||
descriptionKey?: string;
|
||||
|
||||
/**
|
||||
* An optional initial value to initiate the field with.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { randomInt } from '@jitsi/js-utils/random';
|
||||
import React, { Component } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
@@ -11,7 +11,6 @@ import { isFatalJitsiConnectionError } from '../../../lib-jitsi-meet/functions.n
|
||||
import { hideDialog } from '../../actions';
|
||||
import logger from '../../logger';
|
||||
|
||||
// @ts-ignore
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
|
||||
|
||||
@@ -39,9 +38,7 @@ interface IPageReloadDialogState {
|
||||
* Shows a warning message and counts down towards the re-load.
|
||||
*/
|
||||
class PageReloadDialog extends Component<IPageReloadDialogProps, IPageReloadDialogState> {
|
||||
|
||||
// @ts-ignore
|
||||
_interval: IntervalID;
|
||||
_interval?: number;
|
||||
_timeoutSeconds: number;
|
||||
|
||||
/**
|
||||
@@ -105,7 +102,7 @@ class PageReloadDialog extends Component<IPageReloadDialogProps, IPageReloadDial
|
||||
_onCancel() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
clearInterval(this._interval);
|
||||
clearInterval(this._interval ?? 0);
|
||||
dispatch(appNavigate(undefined));
|
||||
|
||||
return true;
|
||||
@@ -145,7 +142,7 @@ class PageReloadDialog extends Component<IPageReloadDialogProps, IPageReloadDial
|
||||
_onReloadNow() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
clearInterval(this._interval);
|
||||
clearInterval(this._interval ?? 0);
|
||||
dispatch(reloadNow());
|
||||
|
||||
return true;
|
||||
@@ -200,8 +197,6 @@ function mapStateToProps(state: IReduxState) {
|
||||
const { fatalError } = state['features/overlay'];
|
||||
|
||||
const fatalConnectionError
|
||||
|
||||
// @ts-ignore
|
||||
= connectionError && isFatalJitsiConnectionError(connectionError);
|
||||
const fatalConfigError = fatalError === configError;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Bourne from '@hapi/bourne';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
|
||||
|
||||
import { browser } from '../lib-jitsi-meet';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import jwtDecode from 'jwt-decode';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import jwtDecode from 'jwt-decode';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { isOnline } from '../net-info/selectors';
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from './_';
|
||||
import {
|
||||
LIB_DID_DISPOSE,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IStateful } from '../app/types';
|
||||
import { ConnectionFailedError } from '../connection/actions.any';
|
||||
import { toState } from '../redux/functions';
|
||||
|
||||
// @ts-ignore
|
||||
@@ -91,7 +92,7 @@ export function isFatalJitsiConferenceError(error: Error | string) {
|
||||
* indicates a fatal {@code JitsiConnection} error, {@code true}; otherwise,
|
||||
* {@code false}.
|
||||
*/
|
||||
export function isFatalJitsiConnectionError(error: Error | string) {
|
||||
export function isFatalJitsiConnectionError(error: Error | string | ConnectionFailedError) {
|
||||
if (typeof error !== 'string') {
|
||||
error = error.name; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Bourne from '@hapi/bourne';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet.
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from './_';
|
||||
export { JitsiMeetJS as default };
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { SET_NETWORK_INFO } from '../net-info/actionTypes';
|
||||
import { PARTICIPANT_LEFT } from '../participants/actionTypes';
|
||||
import MiddlewareRegistry from '../redux/MiddlewareRegistry';
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from './_';
|
||||
import { LIB_WILL_INIT } from './actionTypes';
|
||||
import { disposeLib, initLib } from './actions';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { format } from 'util';
|
||||
|
||||
// Some code adapted from https://github.com/houserater/react-native-lumberjack
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Logger, { getLogger as _getLogger } from '@jitsi/logger';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
// @ts-ignore
|
||||
export { default as Audio } from './native/Audio';
|
||||
|
||||
// @ts-ignore
|
||||
export { default as Video } from './native/Video';
|
||||
|
||||
@@ -7,7 +7,6 @@ import { IReduxState, IStore } from '../../../../app/types';
|
||||
import { ASPECT_RATIO_WIDE } from '../../../responsive-ui/constants';
|
||||
import { storeVideoTransform } from '../../actions';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
@@ -125,12 +124,12 @@ class VideoTransform extends Component<IProps, IState> {
|
||||
/**
|
||||
* The gesture handler object.
|
||||
*/
|
||||
gestureHandlers: Object;
|
||||
gestureHandlers: any;
|
||||
|
||||
/**
|
||||
* The initial distance of the fingers on pinch start.
|
||||
*/
|
||||
initialDistance: number;
|
||||
initialDistance?: number;
|
||||
|
||||
/**
|
||||
* The initial position of the finger on touch start.
|
||||
@@ -234,8 +233,6 @@ class VideoTransform extends Component<IProps, IState> {
|
||||
videoTransformedViewContainerStyles,
|
||||
style
|
||||
] }
|
||||
|
||||
// @ts-ignore
|
||||
{ ...this.gestureHandlers.panHandlers }>
|
||||
<SafeAreaView
|
||||
edges = { [ 'bottom', 'left' ] }
|
||||
@@ -489,7 +486,7 @@ class VideoTransform extends Component<IProps, IState> {
|
||||
* @param {?Object | number} value - The value of the gesture, if any.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onGesture(type: string, value: any) {
|
||||
_onGesture(type: string, value?: any) {
|
||||
let transform;
|
||||
|
||||
switch (type) {
|
||||
@@ -600,7 +597,7 @@ class VideoTransform extends Component<IProps, IState> {
|
||||
this._onGesture('scale', scale);
|
||||
}
|
||||
} else if (gestureState.numberActiveTouches === 1
|
||||
&& isNaN(this.initialDistance)
|
||||
&& isNaN(this.initialDistance ?? 0)
|
||||
&& this._didMove(gestureState)) {
|
||||
// this is a move event
|
||||
const position = this._getTouchPosition(evt);
|
||||
@@ -623,11 +620,9 @@ class VideoTransform extends Component<IProps, IState> {
|
||||
*/
|
||||
_onPanResponderRelease() {
|
||||
if (this.lastTap && Date.now() - this.lastTap < TAP_TIMEOUT_MS) {
|
||||
// @ts-ignore
|
||||
this._onGesture('press');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
delete this.initialDistance;
|
||||
this.initialPosition = {
|
||||
x: 0,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
import type { NetInfoState, NetInfoSubscription } from '@react-native-community/netinfo';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import { ONLINE_STATE_CHANGED_EVENT } from './events';
|
||||
@@ -15,7 +15,7 @@ export default class NetworkInfoService extends EventEmitter {
|
||||
/**
|
||||
* Stores the native subscription for future cleanup.
|
||||
*/
|
||||
_subscription: NetInfoSubscription;
|
||||
_subscription?: NetInfoSubscription;
|
||||
|
||||
/**
|
||||
* Converts library's structure to {@link NetworkInfo} used by jitsi-meet.
|
||||
@@ -26,10 +26,8 @@ export default class NetworkInfoService extends EventEmitter {
|
||||
*/
|
||||
static _convertNetInfoState(netInfoState: NetInfoState): NetworkInfo {
|
||||
return {
|
||||
// @ts-ignore
|
||||
isOnline: netInfoState.isInternetReachable,
|
||||
isOnline: Boolean(netInfoState.isInternetReachable),
|
||||
|
||||
// @ts-ignore
|
||||
details: netInfoState.details,
|
||||
networkType: netInfoState.type
|
||||
};
|
||||
@@ -51,8 +49,7 @@ export default class NetworkInfoService extends EventEmitter {
|
||||
*/
|
||||
start() {
|
||||
this._subscription = NetInfo.addEventListener(netInfoState => {
|
||||
// @ts-ignore
|
||||
this.emit(ONLINE_STATE_CHANGED_EVENT, NetworkInfoService._convertNetInfoState(netInfoState));
|
||||
super.emit(ONLINE_STATE_CHANGED_EVENT, NetworkInfoService._convertNetInfoState(netInfoState));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,8 +61,6 @@ export default class NetworkInfoService extends EventEmitter {
|
||||
stop() {
|
||||
if (this._subscription) {
|
||||
this._subscription();
|
||||
|
||||
// @ts-ignore
|
||||
this._subscription = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ export type NetworkInfo = {
|
||||
* If {@link networkType} is {@link NetInfoStateType.cellular} then it may provide the info about the type of
|
||||
* cellular network.
|
||||
*/
|
||||
cellularGeneration?: NetInfoCellularGeneration;
|
||||
cellularGeneration?: NetInfoCellularGeneration | null;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the connection is expensive.
|
||||
*/
|
||||
isConnectionExpensive?: boolean;
|
||||
};
|
||||
} | null;
|
||||
|
||||
/**
|
||||
* Tells whether or not the internet is reachable.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { getGravatarURL } from '@jitsi/js-utils/avatar';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
|
||||
@@ -6,6 +6,7 @@ import { IReduxState } from '../../../app/types';
|
||||
import DialogPortal from '../../../toolbox/components/web/DialogPortal';
|
||||
import Drawer from '../../../toolbox/components/web/Drawer';
|
||||
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
|
||||
import { isElementInTheViewport } from '../../ui/functions.web';
|
||||
import { getContextMenuStyle } from '../functions.web';
|
||||
|
||||
/**
|
||||
@@ -258,7 +259,16 @@ class Popover extends Component<IProps, IState> {
|
||||
'aria-labelledby': headingId,
|
||||
'aria-label': !headingId && headingLabel ? headingLabel : undefined
|
||||
}}
|
||||
returnFocus = { true }>
|
||||
returnFocus = {
|
||||
|
||||
// If we return the focus to an element outside the viewport the page will scroll to
|
||||
// this element which in our case is undesirable and the element is outside of the
|
||||
// viewport on purpose (to be hidden). For example if we return the focus to the toolbox
|
||||
// when it is hidden the whole page will move up in order to show the toolbox. This is
|
||||
// usually followed up with displaying the toolbox (because now it is on focus) but
|
||||
// because of the animation the whole scenario looks like jumping large video.
|
||||
isElementInTheViewport
|
||||
}>
|
||||
{this._renderContent()}
|
||||
</ReactFocusLock>
|
||||
</DialogPortal>
|
||||
@@ -304,7 +314,8 @@ class Popover extends Component<IProps, IState> {
|
||||
&& !this.props.overflowDrawer
|
||||
&& this._contextMenuRef
|
||||
&& this._contextMenuRef.contains
|
||||
&& !this._contextMenuRef.contains(event.target as Node)) {
|
||||
&& !this._contextMenuRef.contains(event.target as Node)
|
||||
&& !this._containerRef?.current?.contains(event.target as Node)) {
|
||||
this._onHideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ import { getToolbarButtons, isToolbarButtonEnabled } from '../../../config/funct
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
|
||||
import ConnectionStatus from './ConnectionStatus';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import Preview from './Preview';
|
||||
|
||||
interface IProps {
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
// @ts-ignore
|
||||
export { default as Container } from './native/Container';
|
||||
// @ts-ignore
|
||||
export { default as Text } from './native/Text';
|
||||
|
||||
@@ -42,7 +42,7 @@ interface IProps {
|
||||
/**
|
||||
* Style of the animated view.
|
||||
*/
|
||||
style: StyleType;
|
||||
style?: StyleType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Bourne from '@hapi/bourne';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import md5 from 'js-md5';
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../../app/types';
|
||||
@@ -7,9 +5,7 @@ import { translate } from '../../../../base/i18n/functions';
|
||||
import { IconGear } from '../../../../base/icons/svg';
|
||||
import AbstractButton, { IProps as AbstractButtonProps } from '../../../../base/toolbox/components/AbstractButton';
|
||||
import { navigate }
|
||||
// @ts-ignore
|
||||
from '../../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
// @ts-ignore
|
||||
import { screen } from '../../../../mobile/navigation/routes';
|
||||
import { SETTINGS_ENABLED } from '../../../flags/constants';
|
||||
import { getFeatureFlag } from '../../../flags/functions';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export default class ToolboxItem extends AbstractToolboxItem<IProps> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(event?: React.KeyboardEvent) {
|
||||
if (event?.key === 'Enter' || event?.key === ' ') {
|
||||
if (event?.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
|
||||
@@ -33,17 +33,17 @@ interface IProps {
|
||||
/**
|
||||
* Icon of the button.
|
||||
*/
|
||||
icon: Function;
|
||||
icon?: Function;
|
||||
|
||||
/**
|
||||
* Flag used for disabling the small icon.
|
||||
*/
|
||||
iconDisabled: boolean;
|
||||
iconDisabled?: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the icon button.
|
||||
*/
|
||||
iconId: string;
|
||||
iconId?: string;
|
||||
|
||||
/**
|
||||
* Popover close callback.
|
||||
@@ -65,6 +65,11 @@ interface IProps {
|
||||
*/
|
||||
styles?: Object;
|
||||
|
||||
/**
|
||||
* Whether the trigger for open/ close should be click or hover.
|
||||
*/
|
||||
trigger?: 'hover' | 'click';
|
||||
|
||||
/**
|
||||
* Whether or not the popover is visible.
|
||||
*/
|
||||
@@ -77,7 +82,7 @@ interface IProps {
|
||||
* @param {Object} props - Component's props.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
export default function ToolboxButtonWithIconPopup(props: IProps) {
|
||||
export default function ToolboxButtonWithPopup(props: IProps) {
|
||||
const {
|
||||
ariaControls,
|
||||
ariaExpanded,
|
||||
@@ -91,9 +96,29 @@ export default function ToolboxButtonWithIconPopup(props: IProps) {
|
||||
onPopoverOpen,
|
||||
popoverContent,
|
||||
styles,
|
||||
trigger,
|
||||
visible
|
||||
} = props;
|
||||
|
||||
if (!icon) {
|
||||
return (
|
||||
<div
|
||||
className = 'settings-button-container'
|
||||
style = { styles }>
|
||||
<Popover
|
||||
content = { popoverContent }
|
||||
headingLabel = { ariaLabel }
|
||||
onPopoverClose = { onPopoverClose }
|
||||
onPopoverOpen = { onPopoverOpen }
|
||||
position = 'top'
|
||||
trigger = { trigger }
|
||||
visible = { visible }>
|
||||
{children}
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const iconProps: any = {};
|
||||
|
||||
if (iconDisabled) {
|
||||
@@ -1,6 +1,4 @@
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
|
||||
import { showNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
|
||||
@@ -10,8 +10,6 @@ import { setScreenAudioShareState, setScreenshareAudioTrack } from '../../screen
|
||||
import { isAudioOnlySharing, isScreenVideoShared } from '../../screen-share/functions';
|
||||
import { toggleScreenshotCaptureSummary } from '../../screenshot-capture/actions';
|
||||
import { isScreenshotCaptureEnabled } from '../../screenshot-capture/functions';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { getCurrentConference } from '../conference/functions';
|
||||
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { IStore } from '../../app/types';
|
||||
import { IStateful } from '../app/types';
|
||||
import { isMobileBrowser } from '../environment/utils';
|
||||
import JitsiMeetJS from '../lib-jitsi-meet';
|
||||
import JitsiMeetJS, { JitsiTrackErrors, browser } from '../lib-jitsi-meet';
|
||||
import { setAudioMuted } from '../media/actions';
|
||||
import { MEDIA_TYPE } from '../media/constants';
|
||||
import { getStartWithAudioMuted } from '../media/functions';
|
||||
import { toState } from '../redux/functions';
|
||||
import {
|
||||
getUserSelectedCameraDeviceId,
|
||||
@@ -94,10 +96,10 @@ export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing a promise which resolves with the created tracks &
|
||||
* the errors resulting from that process.
|
||||
* Returns an object containing a promise which resolves with the created tracks and the errors resulting from that
|
||||
* process.
|
||||
*
|
||||
* @returns {Promise<JitsiLocalTrack>}
|
||||
* @returns {Promise<JitsiLocalTrack[]>}
|
||||
*
|
||||
* @todo Refactor to not use APP.
|
||||
*/
|
||||
@@ -106,7 +108,13 @@ export function createPrejoinTracks() {
|
||||
const initialDevices = [ 'audio' ];
|
||||
const requestedAudio = true;
|
||||
let requestedVideo = false;
|
||||
const { startAudioOnly, startWithAudioMuted, startWithVideoMuted } = APP.store.getState()['features/base/settings'];
|
||||
const { startAudioOnly, startWithVideoMuted } = APP.store.getState()['features/base/settings'];
|
||||
const startWithAudioMuted = getStartWithAudioMuted(APP.store.getState());
|
||||
|
||||
// On Electron there is no permission prompt for granting permissions. That's why we don't need to
|
||||
// spend much time displaying the overlay screen. If GUM is not resolved within 15 seconds it will
|
||||
// probably never resolve.
|
||||
const timeout = browser.isElectron() ? 15000 : 60000;
|
||||
|
||||
// Always get a handle on the audio input device so that we have statistics even if the user joins the
|
||||
// conference muted. Previous implementation would only acquire the handle when the user first unmuted,
|
||||
@@ -121,62 +129,66 @@ export function createPrejoinTracks() {
|
||||
requestedVideo = true;
|
||||
}
|
||||
|
||||
let tryCreateLocalTracks;
|
||||
let tryCreateLocalTracks: any = Promise.resolve([]);
|
||||
|
||||
if (!requestedAudio && !requestedVideo) {
|
||||
// Resolve with no tracks
|
||||
tryCreateLocalTracks = Promise.resolve([]);
|
||||
} else {
|
||||
if (requestedAudio || requestedVideo) {
|
||||
tryCreateLocalTracks = createLocalTracksF({
|
||||
devices: initialDevices,
|
||||
firePermissionPromptIsShownEvent: true
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
timeout
|
||||
}, APP.store)
|
||||
.catch((err: Error) => {
|
||||
if (requestedAudio && requestedVideo) {
|
||||
.catch(async (err: Error) => {
|
||||
if (err.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
|
||||
errors.audioAndVideoError = err;
|
||||
|
||||
// Try audio only...
|
||||
errors.audioAndVideoError = err;
|
||||
return [];
|
||||
}
|
||||
|
||||
return (
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
firePermissionPromptIsShownEvent: true
|
||||
}));
|
||||
} else if (requestedAudio && !requestedVideo) {
|
||||
errors.audioOnlyError = err;
|
||||
// Retry with separate gUM calls.
|
||||
const gUMPromises: any = [];
|
||||
const tracks: any = [];
|
||||
|
||||
return [];
|
||||
} else if (requestedVideo && !requestedAudio) {
|
||||
errors.videoOnlyError = err;
|
||||
if (requestedAudio) {
|
||||
gUMPromises.push(createLocalTracksF({
|
||||
devices: [ MEDIA_TYPE.AUDIO ],
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
timeout
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
if (requestedVideo) {
|
||||
gUMPromises.push(createLocalTracksF({
|
||||
devices: [ MEDIA_TYPE.VIDEO ],
|
||||
firePermissionPromptIsShownEvent: true,
|
||||
timeout
|
||||
}));
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(gUMPromises);
|
||||
let errorMsg;
|
||||
|
||||
results.forEach((result, idx) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
tracks.push(result.value[0]);
|
||||
} else {
|
||||
errorMsg = result.reason;
|
||||
const isAudio = idx === 0;
|
||||
|
||||
logger.error(`${isAudio ? 'Audio' : 'Video'} track creation failed with error ${errorMsg}`);
|
||||
if (isAudio) {
|
||||
errors.audioOnlyError = errorMsg;
|
||||
} else {
|
||||
errors.videoOnlyError = errorMsg;
|
||||
}
|
||||
logger.error('Should never happen');
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
// Log this just in case...
|
||||
if (!requestedAudio) {
|
||||
logger.error('The impossible just happened', err);
|
||||
}
|
||||
errors.audioOnlyError = err;
|
||||
}
|
||||
});
|
||||
|
||||
// Try video only...
|
||||
return requestedVideo
|
||||
? createLocalTracksF({
|
||||
devices: [ 'video' ],
|
||||
firePermissionPromptIsShownEvent: true
|
||||
})
|
||||
: [];
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
// Log this just in case...
|
||||
if (!requestedVideo) {
|
||||
logger.error('The impossible just happened', err);
|
||||
}
|
||||
errors.videoOnlyError = err;
|
||||
if (errors.audioOnlyError && errors.videoOnlyError) {
|
||||
errors.audioAndVideoError = errorMsg;
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
return tracks;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,8 +2,6 @@ import React from 'react';
|
||||
import { TouchableRipple } from 'react-native-paper';
|
||||
|
||||
import Icon from '../../../icons/components/Icon';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import styles from '../../../react/components/native/styles';
|
||||
import { IIconButtonProps } from '../../../react/types';
|
||||
import { BUTTON_TYPES } from '../../constants.native';
|
||||
|
||||
@@ -5,6 +5,7 @@ import { keyframes } from 'tss-react';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { withPixelLineHeight } from '../../../styles/functions.web';
|
||||
import { isElementInTheViewport } from '../../functions.web';
|
||||
|
||||
import { DialogTransitionContext } from './DialogTransition';
|
||||
|
||||
@@ -184,7 +185,16 @@ const BaseDialog = ({
|
||||
onClick = { onBackdropClick } />
|
||||
<FocusLock
|
||||
className = { classes.focusLock }
|
||||
returnFocus = { true }>
|
||||
returnFocus = {
|
||||
|
||||
// If we return the focus to an element outside the viewport the page will scroll to
|
||||
// this element which in our case is undesirable and the element is outside of the
|
||||
// viewport on purpose (to be hidden). For example if we return the focus to the toolbox
|
||||
// when it is hidden the whole page will move up in order to show the toolbox. This is
|
||||
// usually followed up with displaying the toolbox (because now it is on focus) but
|
||||
// because of the animation the whole scenario looks like jumping large video.
|
||||
isElementInTheViewport
|
||||
}>
|
||||
<div
|
||||
aria-describedby = { description }
|
||||
aria-labelledby = { title ?? t(titleKey ?? '') }
|
||||
|
||||
@@ -70,7 +70,7 @@ const useStyles = makeStyles()(theme => {
|
||||
'& input[type="checkbox"]': {
|
||||
appearance: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
margin: 0,
|
||||
margin: '3px',
|
||||
font: 'inherit',
|
||||
color: theme.palette.icon03,
|
||||
width: '18px',
|
||||
|
||||
@@ -57,3 +57,28 @@ export const findAncestorByClass = (target: HTMLElement | null, cssClass: string
|
||||
|
||||
return findAncestorByClass(target.parentElement, cssClass);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the passed element is visible in the viewport.
|
||||
*
|
||||
* @param {Element} element - The element.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isElementInTheViewport(element?: Element): boolean {
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!document.body.contains(element)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { innerHeight, innerWidth } = window;
|
||||
const { bottom, left, right, top } = element.getBoundingClientRect();
|
||||
|
||||
if (bottom <= innerHeight && top >= 0 && left >= 0 && right <= innerWidth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import Bourne from '@hapi/bourne';
|
||||
|
||||
import { reportError } from './helpers';
|
||||
|
||||
@@ -6,8 +6,6 @@ import { IStore } from '../app/types';
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
|
||||
import { refreshCalendar } from './actions';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import UpdateCalendarEventDialog from './components/UpdateCalendarEventDialog.native';
|
||||
import { addLinkToCalendarEntry } from './functions.native';
|
||||
|
||||
@@ -40,8 +38,6 @@ export function updateCalendarEvent(eventId: string) {
|
||||
const roomName = generateRoomWithoutSeparator();
|
||||
|
||||
addLinkToCalendarEntry(getState(), eventId, `${defaultUrl}/${roomName}`)
|
||||
|
||||
// @ts-ignore
|
||||
.finally(() => {
|
||||
dispatch(refreshCalendar(false, false));
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import { IStore } from '../app/types';
|
||||
import { IStateful } from '../base/app/types';
|
||||
import { toState } from '../base/redux/functions';
|
||||
@@ -17,11 +16,8 @@ import {
|
||||
} from './constants';
|
||||
import { _updateCalendarEntries } from './functions.web';
|
||||
import logger from './logger';
|
||||
// @ts-ignore
|
||||
import { googleCalendarApi } from './web/googleCalendar';
|
||||
// @ts-ignore
|
||||
import { microsoftCalendarApi } from './web/microsoftCalendar';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
/**
|
||||
* Determines whether the calendar feature is enabled by the web.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import base64js from 'base64-js';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { findWindows } from 'windows-iana';
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
/* eslint-disable lines-around-comment, max-len */
|
||||
|
||||
import { IParticipant } from '../base/participants/types';
|
||||
import { navigate }
|
||||
// @ts-ignore
|
||||
from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
// @ts-ignore
|
||||
import { screen } from '../mobile/navigation/routes';
|
||||
|
||||
import { OPEN_CHAT } from './actionTypes';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
import { Route, useIsFocused } from '@react-navigation/native';
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -9,7 +8,7 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { TabBarLabelCounter } from '../../../mobile/navigation/components/TabBarLabelCounter';
|
||||
import { closeChat } from '../../actions.native';
|
||||
import AbstractChat, {
|
||||
type Props as AbstractProps,
|
||||
IProps as AbstractProps,
|
||||
_mapStateToProps
|
||||
} from '../AbstractChat';
|
||||
|
||||
@@ -18,24 +17,24 @@ import MessageContainer from './MessageContainer';
|
||||
import MessageRecipient from './MessageRecipient';
|
||||
import styles from './styles';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object,
|
||||
navigation: any;
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
route: Object
|
||||
};
|
||||
route: Route<'', { privateMessageRecipient: { name: string; }; }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React native component that renders the chat window (modal) of
|
||||
* the mobile client.
|
||||
*/
|
||||
class Chat extends AbstractChat<Props> {
|
||||
class Chat extends AbstractChat<IProps> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -51,17 +50,16 @@ class Chat extends AbstractChat<Props> {
|
||||
hasBottomTextInput = { true }
|
||||
hasTabNavigator = { true }
|
||||
style = { styles.chatContainer }>
|
||||
{/* @ts-ignore */}
|
||||
<MessageContainer messages = { _messages } />
|
||||
<MessageRecipient privateMessageRecipient = { privateMessageRecipient } />
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
_onSendMessage: (string) => void;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(props => {
|
||||
export default translate(connect(_mapStateToProps)((props: IProps) => {
|
||||
const { _nbUnreadMessages, dispatch, navigation, t } = props;
|
||||
const unreadMessagesNr = _nbUnreadMessages > 0;
|
||||
|
||||
@@ -78,7 +76,9 @@ export default translate(connect(_mapStateToProps)(props => {
|
||||
)
|
||||
});
|
||||
|
||||
return () => isFocused && dispatch(closeChat());
|
||||
return () => {
|
||||
isFocused && dispatch(closeChat());
|
||||
};
|
||||
}, [ isFocused, _nbUnreadMessages ]);
|
||||
|
||||
return (
|
||||
@@ -1,5 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { CHAT_ENABLED } from '../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../base/flags/functions';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
@@ -10,23 +11,23 @@ import { screen } from '../../../mobile/navigation/routes';
|
||||
import { getUnreadPollCount } from '../../../polls/functions';
|
||||
import { getUnreadCount } from '../../functions';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
interface IProps extends AbstractButtonProps {
|
||||
|
||||
/**
|
||||
* True if the polls feature is disabled.
|
||||
*/
|
||||
_isPollsDisabled: boolean,
|
||||
_isPollsDisabled?: boolean;
|
||||
|
||||
/**
|
||||
* The unread message count.
|
||||
*/
|
||||
_unreadMessageCount: number
|
||||
};
|
||||
_unreadMessageCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an {@link AbstractButton} to open the chat screen on mobile.
|
||||
*/
|
||||
class ChatButton extends AbstractButton<Props, *> {
|
||||
class ChatButton extends AbstractButton<IProps> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.chat';
|
||||
icon = IconMessage;
|
||||
label = 'toolbar.chat';
|
||||
@@ -60,9 +61,9 @@ class ChatButton extends AbstractButton<Props, *> {
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
||||
* @returns {Props}
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
|
||||
const { disablePolls } = state['features/base/config'];
|
||||
const { visible = enabled } = ownProps;
|
||||
@@ -25,10 +25,6 @@ class ChatPrivacyDialog extends AbstractChatPrivacyDialog {
|
||||
onSubmit = { this._onSendPrivateMessage } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSendGroupMessage: () => boolean;
|
||||
|
||||
_onSendPrivateMessage: () => boolean;
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(ChatPrivacyDialog));
|
||||
@@ -1,63 +1,65 @@
|
||||
import React from 'react';
|
||||
import { Text, TouchableHighlight, View } from 'react-native';
|
||||
import { Text, TouchableHighlight, View, ViewStyle } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import Icon from '../../../base/icons/components/Icon';
|
||||
import { IconCloseLarge } from '../../../base/icons/svg';
|
||||
import { ILocalParticipant } from '../../../base/participants/types';
|
||||
import {
|
||||
setParams
|
||||
} from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
|
||||
import { setLobbyChatActiveState, setPrivateMessageRecipient } from '../../actions.any';
|
||||
import AbstractMessageRecipient, {
|
||||
type Props as AbstractProps
|
||||
IProps as AbstractProps
|
||||
} from '../AbstractMessageRecipient';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
type Props = AbstractProps & {
|
||||
interface IProps extends AbstractProps {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Is lobby messaging active.
|
||||
*/
|
||||
isLobbyChatActive: boolean,
|
||||
isLobbyChatActive: boolean;
|
||||
|
||||
/**
|
||||
* The participant string for lobby chat messaging.
|
||||
*/
|
||||
lobbyMessageRecipient: Object,
|
||||
lobbyMessageRecipient?: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | ILocalParticipant;
|
||||
|
||||
/**
|
||||
* The participant object set for private messaging.
|
||||
*/
|
||||
privateMessageRecipient: Object,
|
||||
};
|
||||
privateMessageRecipient: { name: string; };
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to implement the displaying of the recipient of the next message.
|
||||
*/
|
||||
class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
class MessageRecipient extends AbstractMessageRecipient<IProps> {
|
||||
|
||||
/**
|
||||
* Constructor of the component.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @param {IProps} props - The props of the component.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this._onResetPrivateMessageRecipient = this._onResetPrivateMessageRecipient.bind(this);
|
||||
this._onResetLobbyMessageRecipient = this._onResetLobbyMessageRecipient.bind(this);
|
||||
}
|
||||
|
||||
_onResetLobbyMessageRecipient: () => void;
|
||||
|
||||
/**
|
||||
* Resets lobby message recipient from state.
|
||||
*
|
||||
@@ -69,8 +71,6 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
dispatch(setLobbyChatActiveState(false));
|
||||
}
|
||||
|
||||
_onResetPrivateMessageRecipient: () => void;
|
||||
|
||||
/**
|
||||
* Resets private message recipient from state.
|
||||
*
|
||||
@@ -102,10 +102,10 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
|
||||
if (isLobbyChatActive) {
|
||||
return (
|
||||
<View style = { styles.lobbyMessageRecipientContainer }>
|
||||
<View style = { styles.lobbyMessageRecipientContainer as ViewStyle }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.lobbyChatMessageTo', {
|
||||
recipient: lobbyMessageRecipient.name
|
||||
recipient: lobbyMessageRecipient?.name
|
||||
}) }
|
||||
</Text>
|
||||
<TouchableHighlight
|
||||
@@ -123,7 +123,7 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.messageRecipientContainer }>
|
||||
<View style = { styles.messageRecipientContainer as ViewStyle }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.messageTo', {
|
||||
recipient: privateMessageRecipient.name
|
||||
@@ -145,9 +145,10 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
* Maps part of the redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
* @param {any} _ownProps - Component's own props.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState, _ownProps: any) {
|
||||
const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat'];
|
||||
|
||||
return {
|
||||
@@ -29,11 +29,11 @@ const styles = (theme: Theme) => {
|
||||
chatMessage: {
|
||||
display: 'inline-flex',
|
||||
padding: '12px',
|
||||
marginRight: '12px',
|
||||
backgroundColor: theme.palette.ui02,
|
||||
borderRadius: '4px 12px 12px 12px',
|
||||
maxWidth: '100%',
|
||||
marginTop: '4px',
|
||||
boxSizing: 'border-box' as const,
|
||||
|
||||
'&.privatemessage': {
|
||||
backgroundColor: theme.palette.support05
|
||||
@@ -62,7 +62,8 @@ const styles = (theme: Theme) => {
|
||||
replyWrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row' as const,
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
maxWidth: '100%'
|
||||
},
|
||||
|
||||
messageContent: {
|
||||
@@ -126,7 +127,7 @@ class ChatMessage extends AbstractChatMessage<IProps> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { classes.chatMessageWrapper }
|
||||
className = { clsx(classes.chatMessageWrapper, type) }
|
||||
id = { this.props.message.messageId }
|
||||
tabIndex = { -1 }>
|
||||
<div
|
||||
|
||||
@@ -25,7 +25,11 @@ const useStyles = makeStyles()(theme => {
|
||||
messageGroup: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: '100%'
|
||||
maxWidth: '100%',
|
||||
|
||||
'&.remote': {
|
||||
maxWidth: 'calc(100% - 40px)' // 100% - avatar and margin
|
||||
}
|
||||
},
|
||||
|
||||
groupContainer: {
|
||||
|
||||
@@ -28,6 +28,13 @@ const useStyles = makeStyles()(theme => {
|
||||
color: theme.palette.text01
|
||||
},
|
||||
|
||||
text: {
|
||||
maxWidth: 'calc(100% - 30px)',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'break-spaces',
|
||||
wordBreak: 'break-all'
|
||||
},
|
||||
|
||||
iconButton: {
|
||||
padding: '2px',
|
||||
|
||||
@@ -72,7 +79,7 @@ const MessageRecipient = ({
|
||||
className = { classes.container }
|
||||
id = 'chat-recipient'
|
||||
role = 'alert'>
|
||||
<span>
|
||||
<span className = { classes.text }>
|
||||
{t(_isLobbyChatActive ? 'chat.lobbyChatMessageTo' : 'chat.messageTo', {
|
||||
recipient: _isLobbyChatActive ? _lobbyMessageRecipient : _privateMessageRecipient
|
||||
})}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import aliases from 'react-emoji-render/data/aliases';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
import emojiAsciiAliases from 'react-emoji-render/data/asciiAliases';
|
||||
|
||||
import { IReduxState } from '../app/types';
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { IStore } from '../app/types';
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import AlertDialog from '../base/dialog/components/native/AlertDialog';
|
||||
import { getParticipantDisplayName } from '../base/participants/functions';
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
|
||||
import { doInvitePeople } from '../../../invite/actions.native';
|
||||
import { getInviteOthersControl } from '../../../share-room/functions';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import React, { useEffect } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, ViewStyle } from 'react-native';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
|
||||
// @ts-ignore
|
||||
import LoadingIndicator from '../../../../base/react/components/native/LoadingIndicator';
|
||||
// @ts-ignore
|
||||
import TintedView from '../../../../base/react/components/native/TintedView';
|
||||
import { isLocalVideoTrackDesktop } from '../../../../base/tracks/functions.native';
|
||||
// @ts-ignore
|
||||
import { setPictureInPictureEnabled } from '../../../../mobile/picture-in-picture/functions';
|
||||
// @ts-ignore
|
||||
import { setIsCarmode } from '../../../../video-layout/actions';
|
||||
// @ts-ignore
|
||||
import ConferenceTimer from '../../ConferenceTimer';
|
||||
// @ts-ignore
|
||||
import { isConnecting } from '../../functions';
|
||||
|
||||
import CarModeFooter from './CarModeFooter';
|
||||
import MicrophoneButton from './MicrophoneButton';
|
||||
import TitleBar from './TitleBar';
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
@@ -66,17 +58,16 @@ const CarMode = (): JSX.Element => {
|
||||
}
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.titleBarSafeViewColor }>
|
||||
style = { styles.titleBarSafeViewColor as ViewStyle }>
|
||||
<View
|
||||
style = { styles.titleBar }>
|
||||
{/* @ts-ignore */}
|
||||
style = { styles.titleBar as ViewStyle }>
|
||||
<TitleBar />
|
||||
</View>
|
||||
<ConferenceTimer textStyle = { styles.roomTimer } />
|
||||
</View>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.microphoneContainer }>
|
||||
style = { styles.microphoneContainer as ViewStyle }>
|
||||
<MicrophoneButton />
|
||||
</View>
|
||||
</JitsiScreen>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
import EndMeetingButton from './EndMeetingButton';
|
||||
import SoundDeviceButton from './SoundDeviceButton';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
@@ -19,7 +17,7 @@ const CarModeFooter = (): JSX.Element => {
|
||||
return (
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.bottomContainer }>
|
||||
style = { styles.bottomContainer as ViewStyle }>
|
||||
<Text style = { styles.videoStoppedLabel }>
|
||||
{ t('carmode.labels.videoStopped') }
|
||||
</Text>
|
||||
|
||||
@@ -8,8 +8,6 @@ import Button from '../../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../../base/ui/constants.native';
|
||||
|
||||
import EndMeetingIcon from './EndMeetingIcon';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { TouchableOpacity, View } from 'react-native';
|
||||
import { TouchableOpacity, View, ViewStyle } from 'react-native';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import {
|
||||
@@ -18,7 +18,6 @@ import { isLocalTrackMuted } from '../../../../base/tracks/functions';
|
||||
import { isAudioMuteButtonDisabled } from '../../../../toolbox/functions.any';
|
||||
import { muteLocal } from '../../../../video-menu/actions';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
const LONG_PRESS = 'long.press';
|
||||
@@ -77,9 +76,9 @@ const MicrophoneButton = (): JSX.Element | null => {
|
||||
style = { [
|
||||
styles.microphoneStyles.container,
|
||||
!audioMuted && styles.microphoneStyles.unmuted
|
||||
] }>
|
||||
] as ViewStyle[] }>
|
||||
<View
|
||||
style = { styles.microphoneStyles.iconContainer }>
|
||||
style = { styles.microphoneStyles.iconContainer as ViewStyle }>
|
||||
<Icon
|
||||
src = { audioMuted ? IconMicSlash : IconMic }
|
||||
style = { styles.microphoneStyles.icon } />
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { openSheet } from '../../../../base/dialog/actions';
|
||||
import Button from '../../../../base/ui/components/native/Button';
|
||||
import { BUTTON_TYPES } from '../../../../base/ui/constants.native';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import AudioRoutePickerDialog from '../../../../mobile/audio-mode/components/AudioRoutePickerDialog';
|
||||
|
||||
import AudioIcon from './AudioIcon';
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
|
||||
import React from 'react';
|
||||
import { StyleProp, Text, View, ViewStyle } from 'react-native';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
@@ -10,14 +8,12 @@ import { MEETING_NAME_ENABLED } from '../../../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../../../base/flags/functions';
|
||||
import { JitsiRecordingConstants } from '../../../../base/lib-jitsi-meet';
|
||||
import { getLocalParticipant } from '../../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import ConnectionIndicator from '../../../../connection-indicator/components/native/ConnectionIndicator';
|
||||
// @ts-ignore
|
||||
import RecordingLabel from '../../../../recording/components/native/RecordingLabel';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import VideoQualityLabel from '../../../../video-quality/components/VideoQualityLabel.native';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
@@ -56,9 +52,7 @@ const TitleBar = (props: IProps): JSX.Element => {
|
||||
<VideoQualityLabel />
|
||||
</View>
|
||||
<ConnectionIndicator
|
||||
// @ts-ignore
|
||||
iconStyle = { styles.connectionIndicatorIcon }
|
||||
// @ts-ignore
|
||||
participantId = { localParticipantId } />
|
||||
<View style = { styles.headerLabels as StyleProp<ViewStyle> }>
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
|
||||
@@ -28,8 +28,6 @@ import { SET_REDUCED_UI } from '../base/responsive-ui/actionTypes';
|
||||
import { BUTTON_TYPES } from '../base/ui/constants.any';
|
||||
import { inIframe } from '../base/util/iframeUtils';
|
||||
import { isCalendarEnabled } from '../calendar-sync/functions';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import FeedbackDialog from '../feedback/components/FeedbackDialog';
|
||||
import { setFilmstripEnabled } from '../filmstrip/actions.any';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
|
||||
@@ -87,7 +87,6 @@ class ConnectionIndicator extends AbstractConnectionIndicator<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
// @ts-ignore
|
||||
this.state = {
|
||||
autoHideTimeout: undefined,
|
||||
showIndicator: false,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { INDICATOR_DISPLAY_THRESHOLD } from '../AbstractConnectionIndicator';
|
||||
|
||||
export const CONNECTOR_INDICATOR_LOST = BaseTheme.palette.ui05;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export default {};
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
/* eslint-disable lines-around-comment */
|
||||
import { IReduxState } from '../app/types';
|
||||
import { isMobileBrowser } from '../base/environment/utils';
|
||||
import Platform from '../base/react/Platform';
|
||||
import { URI_PROTOCOL_PATTERN } from '../base/util/uri';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
|
||||
// @ts-ignore
|
||||
import DeepLinkingDesktopPage from './components/DeepLinkingDesktopPage';
|
||||
// @ts-ignore
|
||||
import DeepLinkingMobilePage from './components/DeepLinkingMobilePage';
|
||||
// @ts-ignore
|
||||
import NoMobileApp from './components/NoMobileApp';
|
||||
import { _openDesktopApp } from './openDesktopApp';
|
||||
/* eslint-enable lines-around-comment */
|
||||
|
||||
/**
|
||||
* Generates a deep linking URL based on the current window URL.
|
||||
|
||||
@@ -12,6 +12,7 @@ import AbstractDialogTab, {
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import { createLocalTrack } from '../../base/lib-jitsi-meet/functions.web';
|
||||
import Checkbox from '../../base/ui/components/web/Checkbox';
|
||||
import { iAmVisitor as iAmVisitorCheck } from '../../visitors/functions';
|
||||
import logger from '../logger';
|
||||
|
||||
import AudioInputPreview from './AudioInputPreview';
|
||||
@@ -92,6 +93,11 @@ interface IProps extends AbstractDialogTabProps, WithTranslation {
|
||||
*/
|
||||
hideNoiseSuppression: boolean;
|
||||
|
||||
/**
|
||||
* Whether we are in visitors mode.
|
||||
*/
|
||||
iAmVisitor: boolean;
|
||||
|
||||
/**
|
||||
* Wether noise suppression is on or not.
|
||||
*/
|
||||
@@ -234,6 +240,7 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
hideAudioOutputPreview,
|
||||
hideDeviceHIDContainer,
|
||||
hideNoiseSuppression,
|
||||
iAmVisitor,
|
||||
noiseSuppressionEnabled,
|
||||
selectedAudioOutputId,
|
||||
t
|
||||
@@ -242,12 +249,12 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
|
||||
return (
|
||||
<div className = { classes.container }>
|
||||
<div
|
||||
{!iAmVisitor && <div
|
||||
aria-live = 'polite'
|
||||
className = { classes.inputContainer }>
|
||||
{this._renderSelector(audioInput)}
|
||||
</div>
|
||||
{!hideAudioInputPreview && hasAudioPermission
|
||||
</div>}
|
||||
{!hideAudioInputPreview && hasAudioPermission && !iAmVisitor
|
||||
&& <AudioInputPreview
|
||||
track = { this.state.previewAudioTrack } />}
|
||||
<div
|
||||
@@ -259,7 +266,7 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
className = { classes.outputButton }
|
||||
deviceId = { selectedAudioOutputId } />}
|
||||
</div>
|
||||
{!hideNoiseSuppression && (
|
||||
{!hideNoiseSuppression && !iAmVisitor && (
|
||||
<div className = { classes.noiseSuppressionContainer }>
|
||||
<Checkbox
|
||||
checked = { noiseSuppressionEnabled }
|
||||
@@ -270,7 +277,7 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
}) } />
|
||||
</div>
|
||||
)}
|
||||
{!hideDeviceHIDContainer
|
||||
{!hideDeviceHIDContainer && !iAmVisitor
|
||||
&& <DeviceHidContainer />}
|
||||
</div>
|
||||
);
|
||||
@@ -380,7 +387,8 @@ class AudioDevicesSelection extends AbstractDialogTab<IProps, IState> {
|
||||
|
||||
const mapStateToProps = (state: IReduxState) => {
|
||||
return {
|
||||
availableDevices: state['features/base/devices'].availableDevices ?? {}
|
||||
availableDevices: state['features/base/devices'].availableDevices ?? {},
|
||||
iAmVisitor: iAmVisitorCheck(state)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from '../../base/lib-jitsi-meet/_.web';
|
||||
|
||||
const JitsiTrackEvents = JitsiMeetJS.events.track;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
|
||||
// @ts-ignore
|
||||
import { DisplayNamePrompt } from './components';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
// @ts-ignore
|
||||
export { default as DisplayNamePrompt } from './native/DisplayNamePrompt';
|
||||
|
||||
@@ -3,7 +3,6 @@ import { isDialogOpen } from '../base/dialog/functions';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import { SETTINGS_UPDATED } from '../base/settings/actionTypes';
|
||||
|
||||
// @ts-ignore
|
||||
import { DisplayNamePrompt } from './components';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { IReduxState } from '../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { setPictureInPictureEnabled } from '../mobile/picture-in-picture/functions';
|
||||
|
||||
const { Dropbox } = NativeModules;
|
||||
@@ -70,8 +68,7 @@ export function getSpaceUsage(token: string, _appKey?: any) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isEnabled(state: IReduxState) {
|
||||
const { dropbox = {} } = state['features/base/config'];
|
||||
const { dropbox = { appKey: undefined } } = state['features/base/config'];
|
||||
|
||||
// @ts-ignore
|
||||
return Boolean(Dropbox?.ENABLED && typeof dropbox.appKey === 'string');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { Component } from 'react';
|
||||
|
||||
export default Component;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// @ts-ignore
|
||||
import conferenceStyles from '../conference/components/native/styles';
|
||||
|
||||
import { SET_TILE_VIEW_DIMENSIONS } from './actionTypes';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import styles from './components/native/styles';
|
||||
import { SQUARE_TILE_ASPECT_RATIO, TILE_MARGIN } from './constants';
|
||||
import { getColumnCount, getTileViewParticipantCount } from './functions.native';
|
||||
@@ -24,7 +21,7 @@ export function setTileViewDimensions() {
|
||||
const { clientHeight: height, clientWidth: width, safeAreaInsets = {} } = state['features/base/responsive-ui'];
|
||||
const { left = 0, right = 0, top = 0, bottom = 0 } = safeAreaInsets;
|
||||
const columns = getColumnCount(state);
|
||||
const rows = Math.ceil(participantCount / columns);
|
||||
const rows = Math.ceil(participantCount / columns); // @ts-ignore
|
||||
const conferenceBorder = conferenceStyles.conference.borderWidth || 0;
|
||||
const heightToUse = height - top - bottom - (2 * conferenceBorder);
|
||||
const widthToUse = width - (TILE_MARGIN * 2) - left - right - (2 * conferenceBorder);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { IconModerator } from '../../../base/icons/svg';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import BaseIndicator from '../../../base/react/components/native/BaseIndicator';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1306,9 +1306,17 @@ function _mapStateToProps(state: IReduxState, ownProps: any): Object {
|
||||
const { thumbnailSize } = state['features/filmstrip'].tileViewDimensions ?? { thumbnailSize: undefined };
|
||||
const {
|
||||
stageFilmstripDimensions = {
|
||||
thumbnailSize: {}
|
||||
thumbnailSize: {
|
||||
height: undefined,
|
||||
width: undefined
|
||||
}
|
||||
},
|
||||
screenshareFilmstripDimensions
|
||||
screenshareFilmstripDimensions = {
|
||||
thumbnailSize: {
|
||||
height: undefined,
|
||||
width: undefined
|
||||
}
|
||||
}
|
||||
} = state['features/filmstrip'];
|
||||
|
||||
size = {
|
||||
@@ -1317,16 +1325,19 @@ function _mapStateToProps(state: IReduxState, ownProps: any): Object {
|
||||
};
|
||||
|
||||
if (filmstripType === FILMSTRIP_TYPE.STAGE) {
|
||||
// @ts-ignore
|
||||
const { width: _width, height: _height } = stageFilmstripDimensions.thumbnailSize;
|
||||
const { width: _width, height: _height } = stageFilmstripDimensions.thumbnailSize ?? {
|
||||
width: undefined,
|
||||
height: undefined };
|
||||
|
||||
size = {
|
||||
_width,
|
||||
_height
|
||||
};
|
||||
} else if (filmstripType === FILMSTRIP_TYPE.SCREENSHARE) {
|
||||
// @ts-ignore
|
||||
const { width: _width, height: _height } = screenshareFilmstripDimensions.thumbnailSize;
|
||||
const { width: _width, height: _height } = screenshareFilmstripDimensions.thumbnailSize ?? {
|
||||
width: undefined,
|
||||
height: undefined
|
||||
};
|
||||
|
||||
size = {
|
||||
_width,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import AudioLevelIndicator from '../../../audio-level-indicator/components/AudioLevelIndicator';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import JitsiMeetJS from '../../../base/lib-jitsi-meet/_';
|
||||
import { ITrack } from '../../../base/tracks/types';
|
||||
|
||||
|
||||
@@ -11,12 +11,9 @@ import Platform from '../base/react/Platform.native';
|
||||
import { toState } from '../base/redux/functions';
|
||||
import { ASPECT_RATIO_NARROW } from '../base/responsive-ui/constants';
|
||||
import { getHideSelfView } from '../base/settings/functions.any';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import conferenceStyles from '../conference/components/native/styles';
|
||||
import { shouldDisplayTileView } from '../video-layout/functions.native';
|
||||
|
||||
// @ts-ignore
|
||||
import styles from './components/native/styles';
|
||||
|
||||
export * from './functions.any';
|
||||
@@ -230,7 +227,7 @@ export function getFilmstripDimensions({
|
||||
};
|
||||
localParticipantVisible?: boolean;
|
||||
}) {
|
||||
const { height, width, margin } = styles.thumbnail;
|
||||
const { height, width, margin } = styles.thumbnail; // @ts-ignore
|
||||
const conferenceBorder = conferenceStyles.conference.borderWidth || 0;
|
||||
const { left = 0, right = 0, top = 0, bottom = 0 } = insets;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user