Compare commits

...

21 Commits

Author SHA1 Message Date
Aaron van Meerten
4ed9d5893b util updates 2019-09-09 08:42:00 -05:00
Aaron van Meerten
07b7f03aa7 Merge pull request #4308 from jitsi/prosody-token-allow-asap-server-override
allows override of asap key server in token utility
2019-06-06 14:26:25 -06:00
Aaron van Meerten
7ce44f85ca changed to using a setter for the asapKeyServer 2019-06-06 15:22:38 -05:00
Aaron van Meerten
41e0d782ce allows override of asap key server in token utility 2019-06-06 14:41:46 -05:00
Aaron van Meerten
2a8fafdd36 Merge pull request #4303 from jitsi/start-silent
Adds a config param startSilent to disable audio output.
2019-06-05 12:29:57 -06:00
damencho
faee1c139e Adds a config param startSilent to disable audio output. 2019-06-05 18:01:18 +01:00
virtuacoplenny
eb644987ce fix(settings): do not use non-set localStorage values (#4299)
If a value is not set in localStorage then null is
returned. null should not be converted to an empty
string (via _.escape) because that will then be
stored in localStorage as the user set preference
and will keep overriding any other values set
in localStorage for the displayname.
2019-06-04 17:42:48 -07:00
damencho
de60a70daf Commit from translate.jitsi.org by user damencho.: 555 of 625 strings translated (8 fuzzy). 2019-06-03 11:19:10 +00:00
damencho
2904dfa794 Commit from translate.jitsi.org by user damencho.: 584 of 625 strings translated (2 fuzzy). 2019-06-03 11:19:01 +00:00
damencho
d51cf7c581 Commit from translate.jitsi.org by user damencho.: 625 of 625 strings translated (0 fuzzy). 2019-06-03 10:58:25 +00:00
Hristo Terezov
f25e6c6a5d chore(package.json): Update lib-jitsi-meet version 2019-06-01 02:28:04 -07:00
Hristo Terezov
5fb9422513 feat(API): Add show feedback parameter to hangup 2019-06-01 02:28:04 -07:00
Hristo Terezov
d01cfc8466 fix(conference): API left event. 2019-06-01 02:28:04 -07:00
Saúl Ibarra Corretgé
fa3888991f rn: avoid logging initial props in release builds
They may contain sensitive information.
2019-05-31 11:49:36 +02:00
virtuacoplenny
ded355a807 fix(settings): use moderator check helper (#4292) 2019-05-30 14:10:40 -07:00
Leonard Kim
b655c8d54a fix(large-video): clear remote video stream on track removal
VideoLayout schedules a large video update by passing in
the video stream on the small video instance. When a stream
is removed, the UI is removed from the small video instance
but a reference to the stream is left. So when VideoLayout
schedules the large video update after a stream removal,
the old stream from the small video instance is re-used,
even though it has been removed.

This change also brings balance with RemoteVideo method
"addRemoteStreamElement" which sets the stream on the
small video instance, so now "removeRemoteStreamElement
unsets it.
2019-05-30 09:46:35 -07:00
Leonard Kim
42a6e6faaf ref(large-video): remove redundant call to update on stream removal 2019-05-30 09:46:35 -07:00
Leonard Kim
c7954c284d ref(large-video): move layout update on stream removal to layout middleware 2019-05-30 09:46:35 -07:00
virtuacoplenny
251da1861a feat(api): notify api of mic and camera errors (#4289)
- Use actions to notify the rest of the app that
  a mic or camera error has occurred
- Use middleware to respond to those notifications
  of errors by showing in-app notifications and
  notifying the external api
2019-05-29 14:17:07 -07:00
Hristo Terezov
9712804040 fix(Amplitude): user id 2019-05-29 09:53:31 -07:00
Hristo Terezov
fecbef0aff fix(AmplitudeModule): class name 2019-05-29 17:22:50 +02:00
43 changed files with 2618 additions and 1663 deletions

View File

@@ -52,6 +52,17 @@ class AmplitudeModule
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
}
/**
* Sets the user ID for an Amplitude instance.
*
* @param instanceName The name of the Amplitude instance.
* @param userId The new value for the user ID.
*/
@ReactMethod
public void setUserId(String instanceName, String userId) {
Amplitude.getInstance(instanceName).setUserId(userId);
}
/**
* Sets the user properties for an Amplitude instance.
*

View File

@@ -66,7 +66,7 @@ class ReactInstanceManagerHolder {
}
try {
Class<?> amplitudeModuleClass = Class.forName("AmplitudeModule");
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
} catch (Exception e) {

View File

@@ -51,6 +51,8 @@ import {
import {
checkAndNotifyForNewDevice,
getAvailableDevices,
notifyCameraError,
notifyMicError,
setAudioOutputDeviceId,
updateDeviceList
} from './react/features/base/devices';
@@ -486,10 +488,13 @@ class ConferenceConnector {
* call in hangup() to resolve when all operations are finished.
*/
function disconnect() {
connection.disconnect();
APP.API.notifyConferenceLeft(APP.conference.roomName);
const onDisconnected = () => {
APP.API.notifyConferenceLeft(APP.conference.roomName);
return Promise.resolve();
return Promise.resolve();
};
return connection.disconnect().then(onDisconnected, onDisconnected);
}
/**
@@ -694,13 +699,14 @@ export default {
// If both requests for 'audio' + 'video' and 'audio'
// only failed, we assume that there are some problems
// with user's microphone and show corresponding dialog.
APP.UI.showMicErrorNotification(audioOnlyError);
APP.UI.showCameraErrorNotification(videoOnlyError);
APP.store.dispatch(notifyMicError(audioOnlyError));
APP.store.dispatch(notifyCameraError(videoOnlyError));
} else {
// If request for 'audio' + 'video' failed, but request
// for 'audio' only was OK, we assume that we had
// problems with camera and show corresponding dialog.
APP.UI.showCameraErrorNotification(audioAndVideoError);
APP.store.dispatch(
notifyCameraError(audioAndVideoError));
}
}
@@ -786,6 +792,13 @@ export default {
this.recorder = new Recorder();
}
if (config.startSilent) {
APP.store.dispatch(showNotification({
descriptionKey: 'notify.startSilentDescription',
titleKey: 'notify.startSilentTitle'
}));
}
// XXX The API will take care of disconnecting from the XMPP
// server (and, thus, leaving the room) on unload.
return new Promise((resolve, reject) => {
@@ -839,7 +852,7 @@ export default {
if (!this.localAudio && !mute) {
const maybeShowErrorDialog = error => {
showUI && APP.UI.showMicErrorNotification(error);
showUI && APP.store.dispatch(notifyMicError(error));
};
createLocalTracksF({ devices: [ 'audio' ] }, false)
@@ -902,7 +915,7 @@ export default {
if (!this.localVideo && !mute) {
const maybeShowErrorDialog = error => {
showUI && APP.UI.showCameraErrorNotification(error);
showUI && APP.store.dispatch(notifyCameraError(error));
};
// Try to create local video if there wasn't any.
@@ -2109,7 +2122,7 @@ export default {
this._updateVideoDeviceId();
})
.catch(err => {
APP.UI.showCameraErrorNotification(err);
APP.store.dispatch(notifyCameraError(err));
});
}
);
@@ -2142,7 +2155,7 @@ export default {
this._updateAudioDeviceId();
})
.catch(err => {
APP.UI.showMicErrorNotification(err);
APP.store.dispatch(notifyMicError(err));
});
}
);
@@ -2600,8 +2613,7 @@ export default {
leaveRoomAndDisconnect() {
APP.store.dispatch(conferenceWillLeave(room));
return room.leave()
.then(disconnect, disconnect);
return room.leave().then(disconnect, disconnect);
},
/**

View File

@@ -90,6 +90,10 @@ var config = {
// applied locally. FIXME: having these 2 options is confusing.
// startWithAudioMuted: false,
// Enabling it (with #params) will disable local audio output of remote
// participants and to enable it back a reload is needed.
// startSilent: false
// Video
// Sets the preferred resolution (height) for local video. Defaults to 720.

View File

@@ -264,6 +264,14 @@ The `event` parameter is a String object with the name of the event.
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
The following events are currently supported:
* **cameraError** - event notifications about Jitsi-Meet having failed to access the camera. The listener will receive an object with the following structure:
```javascript
{
type: string, // A constant representing the overall type of the error.
message: string // Additional information about the error.
}
```
* **avatarChanged** - event notifications about avatar
changes. The listener will receive an object with the following structure:
```javascript
@@ -287,6 +295,14 @@ changes. The listener will receive an object with the following structure:
}
```
* **micError** - event notifications about Jitsi-Meet having failed to access the mic. The listener will receive an object with the following structure:
```javascript
{
type: string, // A constant representing the overall type of the error.
message: string // Additional information about the error.
}
```
* **screenSharingStatusChanged** - receives event notifications about turning on/off the local user screen sharing. The listener will receive object with the following structure:
```javascript
{

View File

@@ -44,6 +44,7 @@
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */; };
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAFA778229EAD520033A7FA /* RNRootView.m */; };
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
@@ -96,6 +97,8 @@
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetConferenceOptions.h; sourceTree = "<group>"; };
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetConferenceOptions.m; sourceTree = "<group>"; };
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetConferenceOptions+Private.h"; sourceTree = "<group>"; };
DEAFA777229EAD3B0033A7FA /* RNRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNRootView.h; sourceTree = "<group>"; };
DEAFA778229EAD520033A7FA /* RNRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNRootView.m; sourceTree = "<group>"; };
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
@@ -173,6 +176,8 @@
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */,
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
DEAFA777229EAD3B0033A7FA /* RNRootView.h */,
DEAFA778229EAD520033A7FA /* RNRootView.m */,
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
@@ -478,6 +483,7 @@
files = (
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */,
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */,
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,

View File

@@ -14,6 +14,8 @@
* limitations under the License.
*/
#import <React/RCTBridge.h>
#import "JitsiMeet.h"
@interface JitsiMeet ()

View File

@@ -17,12 +17,11 @@
#include <mach/mach_time.h>
#import <React/RCTRootView.h>
#import "JitsiMeet+Private.h"
#import "JitsiMeetConferenceOptions+Private.h"
#import "JitsiMeetView+Private.h"
#import "ReactUtils.h"
#import "RNRootView.h"
@implementation JitsiMeetView {
@@ -36,7 +35,7 @@
/**
* React Native view where the entire content will be rendered.
*/
RCTRootView *rootView;
RNRootView *rootView;
}
/**
@@ -145,9 +144,9 @@ static void initializeViewsMap() {
} else {
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
rootView
= [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"App"
initialProperties:props];
= [[RNRootView alloc] initWithBridge:bridge
moduleName:@"App"
initialProperties:props];
rootView.backgroundColor = self.backgroundColor;
// Add rootView as a subview which completely covers this one.

20
ios/sdk/src/RNRootView.h Normal file
View File

@@ -0,0 +1,20 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <React/RCTRootView.h>
@interface RNRootView : RCTRootView
@end

45
ios/sdk/src/RNRootView.m Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <React/RCTRootContentView.h>
#import <React/RCTLog.h>
#import "RNRootView.h"
@implementation RNRootView
// Monkey-patch RCTRootView.runApplication to avoid logging initial props.
- (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = [self valueForKey:@"_moduleName"] ?: @"";
RCTRootContentView *_contentView = [self valueForKey:@"_contentView"];
NSNumber *reactTag = [_contentView valueForKey:@"reactTag"];
NSDictionary *appParameters = @{
@"rootTag": reactTag,
@"initialProps": self.appProperties ?: @{},
};
#if DEBUG
RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
#endif
[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
}
@end

View File

@@ -33,6 +33,10 @@ RCT_EXPORT_METHOD(init:(NSString*)instanceName API_KEY:(NSString*)apiKey) {
[[Amplitude instanceWithName:instanceName] initializeApiKey:apiKey];
}
RCT_EXPORT_METHOD(setUserId:(NSString*)instanceName userId: (NSString *) userId) {
[[Amplitude instanceWithName:instanceName] setUserId:userId];
}
RCT_EXPORT_METHOD(setUserProperties:(NSString*)instanceName userPropsString:(NSDictionary*)userProps) {
if (userProps != nil) {
[[Amplitude instanceWithName:instanceName] setUserProperties:userProps];

View File

@@ -1,11 +1,19 @@
{
"en": "Anglés",
"af": "Afrikaans",
"az": "Azèri",
"bg": "Bulgar",
"cs": "Chèc",
"de": "Aleman",
"el": "Grèc",
"eo": "Esperanto",
"es": "Castelhan",
"fr": "Francés",
"hy": "Armenian",
"it": "Italian",
"ja": "Japonés",
"ko": "Corean",
"nb": "Norvegian Bokmål",
"oc": "Occitan",
"pl": "Polonés",
"ptBR": "Portugués (Brasil)",
@@ -14,7 +22,6 @@
"sl": "Eslovèn",
"sv": "Suedés",
"tr": "Turc",
"zhCN": "Chinés (China)",
"nb": "Norvegian Bokmål",
"eo": "Esperanto"
"vi": "Vietnamian",
"zhCN": "Chinés (China)"
}

View File

@@ -1,5 +1,6 @@
{
"en": "Inglês",
"af": "Africâner",
"az": "Azerbaijanês",
"bg": "Búlgaro",
"cs": "Checo",

View File

@@ -1,11 +1,19 @@
{
"en": "Tiếng Anh",
"af": "Tiếng Afrika",
"az": "Tiếng Azecbaizan",
"bg": "Tiếng Bulgaria",
"cs": "Tiếng Séc",
"de": "Tiếng Đức",
"el": "Tiếng Nhật",
"eo": "Tiếng Esperanto",
"es": "Tiếng Tây Ban Nha",
"fr": "Tiếng Pháp",
"hy": "Tiếng Acmenia",
"it": "Tiếng Ý",
"ja": "Tiếng Nhật",
"ko": "Tiếng Hàn",
"nb": "Tiếng Na Uy",
"oc": "Tiếng Occitan",
"pl": "Tiếng Ba Lan",
"ptBR": "Tiếng Bồ Đào Nha (Brazil)",
@@ -14,7 +22,6 @@
"sl": "Tiếng Slovenia",
"sv": "Tiếng Thụy Điển",
"tr": "Tiếng Thổ Nhĩ Kỳ",
"zhCN": "Tiếng Hoa (Trung Quốc)",
"nb": "Tiếng Na Uy",
"eo": "Tiếng Esperanto"
"vi": "Tiếng Việt",
"zhCN": "Tiếng Hoa (Trung Quốc)"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -478,6 +478,8 @@
"mutedTitle": "You're muted!",
"raisedHand": "__name__ would like to speak.",
"somebody": "Somebody",
"startSilentTitle": "You joined with no audio output!",
"startSilentDescription": "Rejoin the meeting to enable audio",
"suboptimalExperienceDescription": "Eer... we are afraid your experience with __appName__ isn't going to be that great here. We are looking for ways to improve this but, until then, please try using one of the <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>.",
"suboptimalExperienceTitle": "Browser Warning",
"newDeviceCameraTitle": "New camera detected",

View File

@@ -103,9 +103,9 @@ function initCommands() {
APP.store.dispatch(toggleTileView());
},
'video-hangup': () => {
'video-hangup': (showFeedbackDialog = true) => {
sendAnalytics(createApiEvent('video.hangup'));
APP.conference.hangup(true);
APP.conference.hangup(showFeedbackDialog);
},
'email': email => {
sendAnalytics(createApiEvent('email.changed'));
@@ -559,6 +559,38 @@ class API {
});
}
/**
* Notify external application of an unexpected camera-related error having
* occurred.
*
* @param {string} type - The type of the camera error.
* @param {string} message - Additional information about the error.
* @returns {void}
*/
notifyOnCameraError(type: string, message: string) {
this._sendEvent({
name: 'camera-error',
type,
message
});
}
/**
* Notify external application of an unexpected mic-related error having
* occurred.
*
* @param {string} type - The type of the mic error.
* @param {string} message - Additional information about the error.
* @returns {void}
*/
notifyOnMicError(type: string, message: string) {
this._sendEvent({
name: 'mic-error',
type,
message
});
}
/**
* Notify external application (if API is enabled) that conference feedback
* has been submitted. Intended to be used in conjunction with the

View File

@@ -51,6 +51,7 @@ const events = {
'avatar-changed': 'avatarChanged',
'audio-availability-changed': 'audioAvailabilityChanged',
'audio-mute-status-changed': 'audioMuteStatusChanged',
'camera-error': 'cameraError',
'device-list-changed': 'deviceListChanged',
'display-name-change': 'displayNameChange',
'email-change': 'emailChange',
@@ -58,6 +59,7 @@ const events = {
'feedback-prompt-displayed': 'feedbackPromptDisplayed',
'filmstrip-display-changed': 'filmstripDisplayChanged',
'incoming-message': 'incomingMessage',
'mic-error': 'micError',
'outgoing-message': 'outgoingMessage',
'participant-joined': 'participantJoined',
'participant-left': 'participantLeft',

View File

@@ -13,16 +13,12 @@ import SharedVideoManager from './shared_video/SharedVideo';
import VideoLayout from './videolayout/VideoLayout';
import Filmstrip from './videolayout/Filmstrip';
import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet';
import { getLocalParticipant } from '../../react/features/base/participants';
import { toggleChat } from '../../react/features/chat';
import { openDisplayNamePrompt } from '../../react/features/display-name';
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
import { setFilmstripVisible } from '../../react/features/filmstrip';
import {
setNotificationsEnabled,
showWarningNotification
} from '../../react/features/notifications';
import { setNotificationsEnabled } from '../../react/features/notifications';
import {
dockToolbox,
setToolboxEnabled,
@@ -40,39 +36,6 @@ UI.eventEmitter = eventEmitter;
let etherpadManager;
let sharedVideoManager;
const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
microphone: {},
camera: {}
};
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]
= 'dialog.cameraUnsupportedResolutionError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.GENERAL]
= 'dialog.cameraUnknownError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.PERMISSION_DENIED]
= 'dialog.cameraPermissionDeniedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.NOT_FOUND]
= 'dialog.cameraNotFoundError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.CONSTRAINT_FAILED]
= 'dialog.cameraConstraintFailedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
= 'dialog.cameraNotSendingData';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[JitsiTrackErrors.GENERAL]
= 'dialog.micUnknownError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.PERMISSION_DENIED]
= 'dialog.micPermissionDeniedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[JitsiTrackErrors.NOT_FOUND]
= 'dialog.micNotFoundError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.CONSTRAINT_FAILED]
= 'dialog.micConstraintFailedError';
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
= 'dialog.micNotSendingData';
const UIListeners = new Map([
[
UIEvents.ETHERPAD_CLICKED,
@@ -295,12 +258,6 @@ UI.addLocalStream = track => {
}
};
/**
* Removed remote stream from UI.
* @param {JitsiTrack} track stream to remove
*/
UI.removeRemoteStream = track => VideoLayout.onRemoteStreamRemoved(track);
/**
* Setup and show Etherpad.
* @param {string} name etherpad id
@@ -774,65 +731,6 @@ UI.showExtensionInlineInstallationDialog = function(callback) {
});
};
/**
* Shows a notifications about the passed in microphone error.
*
* @param {JitsiTrackError} micError - An error object related to using or
* acquiring an audio stream.
* @returns {void}
*/
UI.showMicErrorNotification = function(micError) {
if (!micError) {
return;
}
const { message, name } = micError;
const micJitsiTrackErrorMsg
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[name];
const micErrorMsg = micJitsiTrackErrorMsg
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.GENERAL];
const additionalMicErrorMsg = micJitsiTrackErrorMsg ? null : message;
APP.store.dispatch(showWarningNotification({
description: additionalMicErrorMsg,
descriptionKey: micErrorMsg,
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
? 'deviceError.microphonePermission'
: 'deviceError.microphoneError'
}));
};
/**
* Shows a notifications about the passed in camera error.
*
* @param {JitsiTrackError} cameraError - An error object related to using or
* acquiring a video stream.
* @returns {void}
*/
UI.showCameraErrorNotification = function(cameraError) {
if (!cameraError) {
return;
}
const { message, name } = cameraError;
const cameraJitsiTrackErrorMsg
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[name];
const cameraErrorMsg = cameraJitsiTrackErrorMsg
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.GENERAL];
const additionalCameraErrorMsg = cameraJitsiTrackErrorMsg ? null : message;
APP.store.dispatch(showWarningNotification({
description: additionalCameraErrorMsg,
descriptionKey: cameraErrorMsg,
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
? 'deviceError.cameraPermission' : 'deviceError.cameraError'
}));
};
/**
* Shows error dialog that informs the user that no data is received from the
* device.

View File

@@ -357,6 +357,11 @@ RemoteVideo.prototype.removeRemoteStreamElement = function(stream) {
logger.info(`${isVideo ? 'Video' : 'Audio'
} removed ${this.id}`, select);
if (stream === this.videoStream) {
this.videoStream = null;
}
this.updateView();
};

View File

@@ -1,4 +1,4 @@
/* global $, APP, interfaceConfig */
/* global $, APP, config, interfaceConfig */
/* eslint-disable no-unused-vars */
import React from 'react';
@@ -199,6 +199,8 @@ SmallVideo.createStreamElement = function(stream) {
if (isVideo) {
element.setAttribute('muted', 'true');
} else if (config.startSilent) {
element.muted = true;
}
element.autoplay = true;

View File

@@ -348,10 +348,6 @@ const VideoLayout = {
remoteVideo.removeRemoteStreamElement(stream);
}
if (stream.isVideoTrack()) {
this._updateLargeVideoIfDisplayed(id);
}
this.updateMutedForNoTracks(id, stream.getType());
},

View File

@@ -1,6 +1,10 @@
/* global APP, JitsiMeetJS */
import { getAudioOutputDeviceId } from '../../react/features/base/devices';
import {
getAudioOutputDeviceId,
notifyCameraError,
notifyMicError
} from '../../react/features/base/devices';
import {
getUserSelectedCameraDeviceId,
getUserSelectedMicDeviceId,
@@ -176,11 +180,11 @@ export default {
]))
.then(tracks => {
if (audioTrackError) {
APP.UI.showMicErrorNotification(audioTrackError);
APP.store.dispatch(notifyMicError(audioTrackError));
}
if (videoTrackError) {
APP.UI.showCameraErrorNotification(videoTrackError);
APP.store.dispatch(notifyCameraError(videoTrackError));
}
return tracks.filter(t => typeof t !== 'undefined');
@@ -205,7 +209,7 @@ export default {
})
.catch(err => {
audioTrackError = err;
showError && APP.UI.showMicErrorNotification(err);
showError && APP.store.disptach(notifyMicError(err));
return [];
}));
@@ -223,7 +227,7 @@ export default {
})
.catch(err => {
videoTrackError = err;
showError && APP.UI.showCameraErrorNotification(err);
showError && APP.store.dispatch(notifyCameraError(err));
return [];
}));

4
package-lock.json generated
View File

@@ -8927,8 +8927,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
"from": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
"version": "github:jitsi/lib-jitsi-meet#0ee30bf12a549d10bb5d559e19bd557c3ed179eb",
"from": "github:jitsi/lib-jitsi-meet#0ee30bf12a549d10bb5d559e19bd557c3ed179eb",
"requires": {
"@jitsi/sdp-interop": "0.1.14",
"@jitsi/sdp-simulcast": "0.2.1",

View File

@@ -52,7 +52,7 @@
"js-utils": "github:jitsi/js-utils#73a67a7a60d52f8e895f50939c8fcbd1f20fe7b5",
"jsrsasign": "8.0.12",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#0ee30bf12a549d10bb5d559e19bd557c3ed179eb",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.11",
"moment": "2.19.4",

View File

@@ -15,7 +15,7 @@ export default class AmplitudeHandler extends AbstractHandler {
constructor(options) {
super();
const { amplitudeAPPKey, host } = options;
const { amplitudeAPPKey, host, user } = options;
if (!amplitudeAPPKey) {
throw new Error('Failed to initialize Amplitude handler, no APP key');
@@ -28,6 +28,10 @@ export default class AmplitudeHandler extends AbstractHandler {
};
amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey);
if (user) {
amplitude.getInstance(this._amplitudeOptions).setUserId(user);
}
}
/**

View File

@@ -31,6 +31,16 @@ class Amplitude {
AmplitudeNative.init(this._instanceName, apiKey);
}
/**
* Sets an identifier for the current user.
*
* @param {string} userId - The new user id.
* @returns {void}
*/
setUserId(userId) {
AmplitudeNative.setUserId(this._instanceName, userId);
}
/**
* Sets user properties for the current user.
*

View File

@@ -6,6 +6,7 @@ import React from 'react';
import { DialogContainer } from '../../base/dialog';
import '../../base/responsive-ui';
import '../../chat';
import '../../external-api';
import '../../room-lock';
import '../../video-layout';

View File

@@ -132,6 +132,7 @@ const WHITELISTED_KEYS = [
'startAudioMuted',
'startAudioOnly',
'startBitrate',
'startSilent',
'startScreenSharing',
'startVideoMuted',
'startWithAudioMuted',

View File

@@ -1,3 +1,25 @@
/**
* The type of Redux action which signals that an error occurred while obtaining
* a camera.
*
* {
* type: NOTIFY_CAMERA_ERROR,
* error: Object
* }
*/
export const NOTIFY_CAMERA_ERROR = 'NOTIFY_CAMERA_ERROR';
/**
* The type of Redux action which signals that an error occurred while obtaining
* a microphone.
*
* {
* type: NOTIFY_MIC_ERROR,
* error: Object
* }
*/
export const NOTIFY_MIC_ERROR = 'NOTIFY_MIC_ERROR';
/**
* The type of Redux action which signals that the currently used audio
* input device should be changed.

View File

@@ -7,6 +7,8 @@ import {
import {
ADD_PENDING_DEVICE_REQUEST,
CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
NOTIFY_CAMERA_ERROR,
NOTIFY_MIC_ERROR,
REMOVE_PENDING_DEVICE_REQUESTS,
SET_AUDIO_INPUT_DEVICE,
SET_VIDEO_INPUT_DEVICE,
@@ -148,6 +150,43 @@ export function getAvailableDevices() {
});
}
/**
* Signals that an error occurred while trying to obtain a track from a camera.
*
* @param {Object} error - The device error, as provided by lib-jitsi-meet.
* @param {string} error.name - The constant for the type of the error.
* @param {string} error.message - Optional additional information about the
* error.
* @returns {{
* type: NOTIFY_CAMERA_ERROR,
* error: Object
* }}
*/
export function notifyCameraError(error) {
return {
type: NOTIFY_CAMERA_ERROR,
error
};
}
/**
* Signals that an error occurred while trying to obtain a track from a mic.
*
* @param {Object} error - The device error, as provided by lib-jitsi-meet.
* @param {Object} error.name - The constant for the type of the error.
* @param {string} error.message - Optional additional information about the
* error.
* @returns {{
* type: NOTIFY_MIC_ERROR,
* error: Object
* }}
*/
export function notifyMicError(error) {
return {
type: NOTIFY_MIC_ERROR,
error
};
}
/**
* Remove all pending device requests.

View File

@@ -4,6 +4,7 @@ import { CONFERENCE_JOINED } from '../conference';
import { processExternalDeviceRequest } from '../../device-selection';
import { MiddlewareRegistry } from '../redux';
import UIEvents from '../../../../service/UI/UIEvents';
import { JitsiTrackErrors } from '../lib-jitsi-meet';
import {
removePendingDeviceRequests,
@@ -12,15 +13,35 @@ import {
} from './actions';
import {
CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
NOTIFY_CAMERA_ERROR,
NOTIFY_MIC_ERROR,
SET_AUDIO_INPUT_DEVICE,
SET_VIDEO_INPUT_DEVICE
} from './actionTypes';
import { showNotification } from '../../notifications';
import { showNotification, showWarningNotification } from '../../notifications';
import { updateSettings } from '../settings';
import { setAudioOutputDeviceId } from './functions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
microphone: {
[JitsiTrackErrors.CONSTRAINT_FAILED]: 'dialog.micConstraintFailedError',
[JitsiTrackErrors.GENERAL]: 'dialog.micUnknownError',
[JitsiTrackErrors.NO_DATA_FROM_SOURCE]: 'dialog.micNotSendingData',
[JitsiTrackErrors.NOT_FOUND]: 'dialog.micNotFoundError',
[JitsiTrackErrors.PERMISSION_DENIED]: 'dialog.micPermissionDeniedError'
},
camera: {
[JitsiTrackErrors.CONSTRAINT_FAILED]: 'dialog.cameraConstraintFailedError',
[JitsiTrackErrors.GENERAL]: 'dialog.cameraUnknownError',
[JitsiTrackErrors.NO_DATA_FROM_SOURCE]: 'dialog.cameraNotSendingData',
[JitsiTrackErrors.NOT_FOUND]: 'dialog.cameraNotFoundError',
[JitsiTrackErrors.PERMISSION_DENIED]: 'dialog.cameraPermissionDeniedError',
[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]: 'dialog.cameraUnsupportedResolutionError'
}
};
/**
* Implements the middleware of the feature base/devices.
*
@@ -32,6 +53,53 @@ MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONFERENCE_JOINED:
return _conferenceJoined(store, next, action);
case NOTIFY_CAMERA_ERROR: {
if (typeof APP !== 'object' || !action.error) {
break;
}
const { message, name } = action.error;
const cameraJitsiTrackErrorMsg
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[name];
const cameraErrorMsg = cameraJitsiTrackErrorMsg
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.camera[JitsiTrackErrors.GENERAL];
const additionalCameraErrorMsg = cameraJitsiTrackErrorMsg ? null : message;
store.dispatch(showWarningNotification({
description: additionalCameraErrorMsg,
descriptionKey: cameraErrorMsg,
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
? 'deviceError.cameraPermission' : 'deviceError.cameraError'
}));
break;
}
case NOTIFY_MIC_ERROR: {
if (typeof APP !== 'object' || !action.error) {
break;
}
const { message, name } = action.error;
const micJitsiTrackErrorMsg
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[name];
const micErrorMsg = micJitsiTrackErrorMsg
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
.microphone[JitsiTrackErrors.GENERAL];
const additionalMicErrorMsg = micJitsiTrackErrorMsg ? null : message;
store.dispatch(showWarningNotification({
description: additionalMicErrorMsg,
descriptionKey: micErrorMsg,
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
? 'deviceError.microphonePermission'
: 'deviceError.microphoneError'
}));
break;
}
case SET_AUDIO_INPUT_DEVICE:
APP.UI.emitEvent(UIEvents.AUDIO_DEVICE_CHANGED, action.deviceId);
break;

View File

@@ -119,12 +119,22 @@ function _initSettings(featureState) {
let settings = featureState;
// Old Settings.js values
// FIXME: Let's remove this after a predefined time (e.g. by July 2018) to
// avoid garbage in the source.
const displayName = _.escape(window.localStorage.getItem('displayname'));
const email = _.escape(window.localStorage.getItem('email'));
// FIXME: jibri uses old settings.js local storage values to set its display
// name and email. Provide another way for jibri to set these values, update
// jibri, and remove the old settings.js values.
const savedDisplayName = window.localStorage.getItem('displayname');
const savedEmail = window.localStorage.getItem('email');
let avatarID = _.escape(window.localStorage.getItem('avatarId'));
// The helper _.escape will convert null to an empty strings. The empty
// string will be saved in settings. On app re-load, because an empty string
// is a defined value, it will override any value found in local storage.
// The workaround is sidestepping _.escape when the value is not set in
// local storage.
const displayName
= savedDisplayName === null ? undefined : _.escape(savedDisplayName);
const email = savedEmail === null ? undefined : _.escape(savedEmail);
if (!avatarID) {
// if there is no avatar id, we generate a unique one and use it forever
avatarID = randomHexString(32);

View File

@@ -15,7 +15,6 @@ import UIEvents from '../../../../service/UI/UIEvents';
import { createLocalTracksA } from './actions';
import {
TOGGLE_SCREENSHARING,
TRACK_REMOVED,
TRACK_UPDATED
} from './actionTypes';
import { getLocalTrack, setTrackMuted } from './functions';
@@ -92,14 +91,6 @@ MiddlewareRegistry.register(store => next => action => {
}
break;
case TRACK_REMOVED:
// TODO Remove this middleware case once all UI interested in tracks
// being removed are converted to react and listening for store changes.
if (typeof APP !== 'undefined' && !action.track.local) {
APP.UI.removeRemoteStream(action.track.jitsiTrack);
}
break;
case TRACK_UPDATED:
// TODO Remove the following calls to APP.UI once components interested
// in track mute changes are moved into React and/or redux.

View File

@@ -0,0 +1 @@
import './middleware';

View File

@@ -0,0 +1,30 @@
// @flow
import { NOTIFY_CAMERA_ERROR, NOTIFY_MIC_ERROR } from '../base/devices';
import { MiddlewareRegistry } from '../base/redux';
declare var APP: Object;
/**
* The middleware of the feature {@code external-api}.
*
* @returns {Function}
*/
MiddlewareRegistry.register((/* store */) => next => action => {
switch (action.type) {
case NOTIFY_CAMERA_ERROR:
if (action.error) {
APP.API.notifyOnCameraError(
action.error.name, action.error.message);
}
break;
case NOTIFY_MIC_ERROR:
if (action.error) {
APP.API.notifyOnMicError(action.error.name, action.error.message);
}
break;
}
return next(action);
});

View File

@@ -2,7 +2,10 @@
import { toState } from '../base/redux';
import { parseStandardURIString } from '../base/util';
import { i18next, DEFAULT_LANGUAGE, LANGUAGES } from '../base/i18n';
import { getLocalParticipant, PARTICIPANT_ROLE } from '../base/participants';
import {
getLocalParticipant,
isLocalParticipantModerator
} from '../base/participants';
declare var interfaceConfig: Object;
@@ -83,14 +86,12 @@ export function getMoreTabProps(stateful: Object | Function) {
} = state['features/base/conference'];
const followMeActive = Boolean(state['features/follow-me'].moderator);
const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
const localParticipant = getLocalParticipant(state);
// The settings sections to display.
const showModeratorSettings = Boolean(
conference
&& configuredTabs.includes('moderator')
&& localParticipant.role === PARTICIPANT_ROLE.MODERATOR);
&& isLocalParticipantModerator(state));
return {
currentLanguage: language,

View File

@@ -12,7 +12,7 @@ import {
getParticipantById
} from '../base/participants';
import { MiddlewareRegistry } from '../base/redux';
import { TRACK_ADDED } from '../base/tracks';
import { TRACK_ADDED, TRACK_REMOVED } from '../base/tracks';
import { SET_FILMSTRIP_VISIBLE } from '../filmstrip';
import './middleware.any';
@@ -83,6 +83,12 @@ MiddlewareRegistry.register(store => next => action => {
VideoLayout.onRemoteStreamAdded(action.track.jitsiTrack);
}
break;
case TRACK_REMOVED:
if (!action.track.local) {
VideoLayout.onRemoteStreamRemoved(action.track.jitsiTrack);
}
break;
}

View File

@@ -16,6 +16,8 @@ import { AppRegistry } from 'react-native';
import { App } from './features/app';
import { IncomingCallApp } from './features/mobile/incoming-call';
declare var __DEV__;
/**
* The type of the React {@code Component} props of {@link Root}.
*/
@@ -49,6 +51,26 @@ class Root extends PureComponent<Props> {
}
}
// HORRIBLE HACK ALERT! React Native logs the initial props with `console.log`. Here we are quickly patching it
// to avoid logging potentially sensitive information.
if (!__DEV__) {
/* eslint-disable */
const __orig_console_log = console.log;
const __orig_appregistry_runapplication = AppRegistry.runApplication;
AppRegistry.runApplication = (...args) => {
// $FlowExpectedError
console.log = () => {};
__orig_appregistry_runapplication(...args);
// $FlowExpectedError
console.log = __orig_console_log;
};
/* eslint-enable */
}
// Register the main/root Component of JitsiMeetView.
AppRegistry.registerComponent('App', () => Root);

View File

@@ -100,6 +100,10 @@ function Util.new(module)
return self
end
function Util:set_asap_key_server(asapKeyServer)
self.asapKeyServer = asapKeyServer
end
--- Returns the public key by keyID
-- @param keyId the key ID to request
-- @return the public key (the content of requested resource) or nil
@@ -239,7 +243,6 @@ end
-- @param session the current session
-- @return false and error
function Util:process_and_verify_token(session)
if session.auth_token == nil then
if self.allowEmptyToken then
return true;

View File

@@ -39,6 +39,25 @@ local function room_jid_match_rewrite(room_jid)
return room_jid
end
-- Utility function to check and convert a room JID from real [foo]room1@muc.example.com to virtual room1@muc.foo.example.com
local function room_jid_match_rewrite_from_internal(room_jid)
local node, host, resource = jid.split(room_jid);
if host ~= muc_domain or not node then
module:log("debug", "No need to rewrite %s (not from the MUC host)", room_jid);
return room_jid;
end
local target_subdomain, target_node = node:match("^%[([^%]]+)%](.+)$");
if not (target_node and target_subdomain) then
module:log("debug", "Not rewriting... unexpected node format: %s", node);
return room_jid;
end
-- Ok, rewrite room_jid address to pretty format
local new_node, new_host, new_resource = target_node, muc_domain_prefix..".".. target_subdomain.."."..muc_domain_base, resource;
room_jid = jid.join(new_node, new_host, new_resource);
module:log("debug", "Rewrote to %s", room_jid);
return room_jid
end
--- Finds and returns room by its jid
-- @param room_jid the room jid to search in the muc component
@@ -194,5 +213,6 @@ return {
wrap_async_run = wrap_async_run;
async_handler_wrapper = async_handler_wrapper;
room_jid_match_rewrite = room_jid_match_rewrite;
room_jid_match_rewrite_from_internal = room_jid_match_rewrite_from_internal
update_presence_identity = update_presence_identity;
};