mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-02 04:42:28 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08efd5ecab | ||
|
|
dba1bcb0e3 | ||
|
|
348403abff | ||
|
|
c290cf45b7 | ||
|
|
175c8e6e50 | ||
|
|
f90667b23c | ||
|
|
cf69d591e4 | ||
|
|
e599491583 | ||
|
|
d1520773cf | ||
|
|
573ca97b6c | ||
|
|
0d97f14a1a | ||
|
|
b8f28abfdf | ||
|
|
9ac7c97e67 | ||
|
|
52b3eaacb5 | ||
|
|
9b01ae6db9 | ||
|
|
469487ad36 | ||
|
|
176c3c1601 | ||
|
|
94391234cc | ||
|
|
d84901f196 | ||
|
|
c6b117565d | ||
|
|
2a9124acb5 | ||
|
|
401a783d6a | ||
|
|
39483a30b6 | ||
|
|
0e2a07f8d7 | ||
|
|
36f5b0218d | ||
|
|
a1b3c56de7 | ||
|
|
6d664f133e | ||
|
|
732a433ec1 | ||
|
|
f7dcd1ba2c | ||
|
|
55a8b44224 | ||
|
|
e29db31d91 | ||
|
|
183d3c3ca4 | ||
|
|
c57e713696 | ||
|
|
4519f26adf | ||
|
|
c26f9cc01f | ||
|
|
f6f730b994 |
@@ -91,7 +91,7 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode Integer.parseInt("${version}")
|
||||
versionName "1.2.${version}"
|
||||
versionName "1.3.${version}"
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'x86'
|
||||
}
|
||||
@@ -139,6 +139,7 @@ if (project.hasProperty('JITSI_SIGNING')
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-background-timer')
|
||||
compile project(':react-native-immersive')
|
||||
compile project(':react-native-keep-awake')
|
||||
compile project(':react-native-vector-icons')
|
||||
|
||||
@@ -29,6 +29,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
new com.oblador.vectoricons.VectorIconsPackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new org.jitsi.meet.audiomode.AudioModePackage()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
rootProject.name = 'jitsi-meet-react'
|
||||
|
||||
include ':app'
|
||||
include ':react-native-background-timer'
|
||||
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
|
||||
include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
|
||||
@@ -1229,7 +1229,8 @@ export default {
|
||||
|
||||
room.on(ConferenceEvents.TALK_WHILE_MUTED, () => {
|
||||
APP.UI.showToolbar(6000);
|
||||
UIUtil.animateShowElement($("#talkWhileMutedPopup"), true, 5000);
|
||||
|
||||
APP.UI.showCustomToolbarPopup('#talkWhileMutedPopup', true, 5000);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
@@ -53,7 +53,6 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
disableStats: false,
|
||||
disableAudioLevels: false,
|
||||
channelLastN: -1, // The default value of the channel attribute last-n.
|
||||
adaptiveLastN: false,
|
||||
//disableAdaptiveSimulcast: false,
|
||||
enableRecording: false,
|
||||
enableWelcomePage: true,
|
||||
@@ -80,5 +79,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// Suspending video might cause problems with audio playback. Disabling until these are fixed.
|
||||
disableSuspendVideo: true,
|
||||
// disables or enables RTX (RFC 4588).
|
||||
disableRtx: true
|
||||
disableRtx: true,
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 360.
|
||||
resolution: 720
|
||||
};
|
||||
|
||||
@@ -122,6 +122,7 @@ form {
|
||||
z-index: $tooltipsZ;
|
||||
&-inner {
|
||||
background-color: $tooltipBg;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
|
||||
@@ -1,34 +1,10 @@
|
||||
/*Initialize*/
|
||||
ul.loginmenu {
|
||||
font-family: $baseFontFamily;
|
||||
line-height: normal;
|
||||
display:none;
|
||||
div.loginmenu {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
padding-bottom: 7px;
|
||||
top: 45px;
|
||||
left: -5px;
|
||||
background-color: rgba(0,0,0,0.9);
|
||||
border: 1px solid rgba(256, 256, 256, 0.2);
|
||||
border-radius:8px;
|
||||
}
|
||||
|
||||
ul.loginmenu li {
|
||||
list-style-type: none;
|
||||
padding: 7px;
|
||||
color: #fff;
|
||||
font-size: 11pt;
|
||||
cursor: default;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
ul.loginmenu:after {
|
||||
content: url('../images/dropdownPointer.png');
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 18px;
|
||||
top: 40px;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
a.disabled {
|
||||
@@ -36,23 +12,7 @@ a.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.loginmenuPadding {
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.loginmenu.extendedToolbarPopup {
|
||||
left: 55px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
ul.loginmenu.extendedToolbarPopup:after {
|
||||
content: url('../images/leftDropdownPointer.png');
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: -7px;
|
||||
top: 20px;
|
||||
left: 40px;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ $defaultToolbarSize: 50px;
|
||||
$thumbnailToolbarHeight: 22px;
|
||||
$thumbnailIndicatorBorder: 2px;
|
||||
$thumbnailIndicatorSize: $thumbnailToolbarHeight;
|
||||
$thumbnailVideoMargin: 5px;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailsBorder: 2px;
|
||||
$thumbnailVideoBorder: 2px;
|
||||
$hideFilmstripButtonWidth: 17px;
|
||||
|
||||
@@ -8,4 +8,13 @@
|
||||
text-decoration: underline;
|
||||
@include transition(color .1s ease-in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper links are links that are meant to open a documentation page or more
|
||||
* detailed info.
|
||||
*/
|
||||
.helper-link {
|
||||
@extend .link;
|
||||
font-size: 12px;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 234 B |
Binary file not shown.
|
Before Width: | Height: | Size: 611 B |
@@ -70,5 +70,7 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||
AUDIO_LEVEL_SECONDARY_COLOR: "rgba(255,255,255,0.2)",
|
||||
POLICY_LOGO: null,
|
||||
LOCAL_THUMBNAIL_RATIO: 16/9, //16:9
|
||||
REMOTE_THUMBNAIL_RATIO: 1 //1:1
|
||||
REMOTE_THUMBNAIL_RATIO: 1, //1:1
|
||||
// Documentation reference for the live streaming feature.
|
||||
LIVE_STREAMING_HELP_LINK: "https://jitsi.org/live"
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2</string>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
2602576C1D0A7703001E3363 /* jitsi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2602576B1D0A7703001E3363 /* jitsi.ttf */; };
|
||||
3847F11906B4479A9162628F /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 821D8ABD506944B4BDBB069B /* libRNVectorIcons.a */; };
|
||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
|
||||
7FAD39BE09A84D6AB0ABACA8 /* libRNBackgroundTimer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27A0018BBB2C4FD5A4F9CE71 /* libRNBackgroundTimer.a */; };
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
||||
901FE90FA5744B5B94DCDC41 /* libKCKeepAwake.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EA8C046B2BF46279796F07D /* libKCKeepAwake.a */; };
|
||||
B30EF2311DC0ED7C00690F45 /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B30EF2301DC0ED7C00690F45 /* WebRTC.framework */; };
|
||||
@@ -184,6 +185,13 @@
|
||||
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
|
||||
remoteInfo = RCTText;
|
||||
};
|
||||
B332D04E1E54E3170086EA16 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 0965153BB98645B4A8B6AA10 /* RNBackgroundTimer.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RNBackgroundTimer;
|
||||
};
|
||||
B3BA19D41DC6B37B00BCD481 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
|
||||
@@ -263,6 +271,7 @@
|
||||
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
|
||||
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
|
||||
0965153BB98645B4A8B6AA10 /* RNBackgroundTimer.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNBackgroundTimer.xcodeproj; path = "../node_modules/react-native-background-timer/ios/RNBackgroundTimer.xcodeproj"; sourceTree = "<group>"; };
|
||||
0B42DFAD1E2FD90700111B12 /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AudioMode.m; path = app/AudioMode.m; sourceTree = "<group>"; };
|
||||
0EA8C046B2BF46279796F07D /* libKCKeepAwake.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libKCKeepAwake.a; sourceTree = "<group>"; };
|
||||
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
|
||||
@@ -277,6 +286,7 @@
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
|
||||
22418656B14845609F953A42 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
|
||||
2602576B1D0A7703001E3363 /* jitsi.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = jitsi.ttf; path = ../android/app/src/main/assets/fonts/jitsi.ttf; sourceTree = "<group>"; };
|
||||
27A0018BBB2C4FD5A4F9CE71 /* libRNBackgroundTimer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNBackgroundTimer.a; sourceTree = "<group>"; };
|
||||
5B09C20C78C74A548AAAC1FA /* KCKeepAwake.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = KCKeepAwake.xcodeproj; path = "../node_modules/react-native-keep-awake/ios/KCKeepAwake.xcodeproj"; sourceTree = "<group>"; };
|
||||
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
|
||||
@@ -315,7 +325,7 @@
|
||||
BF9643881C34FBC800B0BBDF /* GLKit.framework in Frameworks */,
|
||||
B30EF2311DC0ED7C00690F45 /* WebRTC.framework in Frameworks */,
|
||||
BF96438E1C34FBE100B0BBDF /* VideoToolbox.framework in Frameworks */,
|
||||
146834051AC3E58100842450 /* libReact.a in Frameworks */,
|
||||
901FE90FA5744B5B94DCDC41 /* libKCKeepAwake.a in Frameworks */,
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
|
||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
|
||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
|
||||
@@ -327,8 +337,9 @@
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
|
||||
BFC745141CB829B300673F38 /* libRCTWebRTC.a in Frameworks */,
|
||||
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
|
||||
146834051AC3E58100842450 /* libReact.a in Frameworks */,
|
||||
7FAD39BE09A84D6AB0ABACA8 /* libRNBackgroundTimer.a in Frameworks */,
|
||||
3847F11906B4479A9162628F /* libRNVectorIcons.a in Frameworks */,
|
||||
901FE90FA5744B5B94DCDC41 /* libKCKeepAwake.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -488,6 +499,7 @@
|
||||
BFC7450D1CB829A700673F38 /* RCTWebRTC.xcodeproj */,
|
||||
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */,
|
||||
0965153BB98645B4A8B6AA10 /* RNBackgroundTimer.xcodeproj */,
|
||||
22418656B14845609F953A42 /* RNVectorIcons.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
@@ -524,6 +536,14 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B332D0301E54E3170086EA16 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B332D04F1E54E3170086EA16 /* libRNBackgroundTimer.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B3BA19B71DC6B02F00BCD481 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -653,6 +673,10 @@
|
||||
ProductGroup = 146834001AC3E56700842450 /* Products */;
|
||||
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = B332D0301E54E3170086EA16 /* Products */;
|
||||
ProjectRef = 0965153BB98645B4A8B6AA10 /* RNBackgroundTimer.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 26D589F81D0B42EE00FC396B /* Products */;
|
||||
ProjectRef = 22418656B14845609F953A42 /* RNVectorIcons.xcodeproj */;
|
||||
@@ -806,6 +830,13 @@
|
||||
remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
B332D04F1E54E3170086EA16 /* libRNBackgroundTimer.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRNBackgroundTimer.a;
|
||||
remoteRef = B332D04E1E54E3170086EA16 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
B3BA19D51DC6B37B00BCD481 /* libRCTImage-tvOS.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
@@ -952,6 +983,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-background-timer/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-keep-awake/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||
);
|
||||
@@ -988,6 +1020,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-background-timer/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-keep-awake/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||
);
|
||||
@@ -1050,6 +1083,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-background-timer/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-keep-awake/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||
);
|
||||
@@ -1095,6 +1129,7 @@
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-background-timer/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-keep-awake/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||
);
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
"sk": "Slowakisch",
|
||||
"sl": "Slowenisch",
|
||||
"sv": "Schwedisch",
|
||||
"tr": "Türkisch"
|
||||
"tr": "Türkisch",
|
||||
"zhCN": "Chinesisch (China)"
|
||||
}
|
||||
17
lang/languages-zhCN.json
Normal file
17
lang/languages-zhCN.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": ""
|
||||
}
|
||||
@@ -13,5 +13,6 @@
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sv": "Swedish",
|
||||
"tr": "Turkish"
|
||||
"tr": "Turkish",
|
||||
"zhCN": "Chinese (China)"
|
||||
}
|
||||
|
||||
@@ -154,7 +154,8 @@
|
||||
"kick": "Hinauswerfen",
|
||||
"muted": "Stummgeschaltet",
|
||||
"domute": "Stummschalten",
|
||||
"flip": "Spiegeln"
|
||||
"flip": "Spiegeln",
|
||||
"remoteControl": "Fernsteuerung"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "Verbindungsdaten",
|
||||
@@ -312,7 +313,12 @@
|
||||
"externalInstallationMsg": "Die Bildschirmfreigabeerweiterung muss installiert werden.",
|
||||
"muteParticipantTitle": "Teilnehmer stummschalten?",
|
||||
"muteParticipantBody": "Sie können die Stummschaltung anderer Teilnehmer nicht aufheben, aber ein Teilnehmer kann seine eigene Stummschaltung jederzeit beenden.",
|
||||
"muteParticipantButton": "Stummschalten"
|
||||
"muteParticipantButton": "Stummschalten",
|
||||
"remoteControlTitle": "Fernsteuerung",
|
||||
"remoteControlDeniedMessage": "__user__ hat die Anfrage zur Fernsteuerung verweigert.",
|
||||
"remoteControlAllowedMessage": "__user__ hat die Anfrage zur Fernsteuerung angenommen.",
|
||||
"remoteControlErrorMessage": "Beim Anfordern der Fernsteuerungsberechtigung von __user__ ist ein Fehler aufgetreten.",
|
||||
"remoteControlStopMessage": "Die Fernsteuerung wurde beendet."
|
||||
},
|
||||
"\u0005dialog": {},
|
||||
"email": {
|
||||
@@ -375,6 +381,7 @@
|
||||
"failedToStart": "Live-Streaming konnte nicht gestartet werden",
|
||||
"buttonTooltip": "Live-Stream starten / stoppen",
|
||||
"streamIdRequired": "Bitte Stream-ID eingeben um das Live-Streaming zu starten.",
|
||||
"streamIdHelp": "Wo ist die Stream-ID zu finden?",
|
||||
"error": "Das Live-Streaming ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"busy": "All Aufnahmegeräte sind momentan ausgelastet. Bitte versuchen Sie es später noch einmal."
|
||||
}
|
||||
|
||||
355
lang/main-zhCN.json
Normal file
355
lang/main-zhCN.json
Normal file
@@ -0,0 +1,355 @@
|
||||
{
|
||||
"contactlist": "",
|
||||
"addParticipants": "",
|
||||
"roomLocked": "",
|
||||
"roomUnlocked": "",
|
||||
"passwordSetRemotely": "",
|
||||
"connectionsettings": "",
|
||||
"poweredby": "",
|
||||
"feedback": "",
|
||||
"inviteUrlDefaultMsg": "",
|
||||
"me": "",
|
||||
"speaker": "",
|
||||
"raisedHand": "",
|
||||
"defaultNickname": "",
|
||||
"defaultLink": "",
|
||||
"callingName": "",
|
||||
"userMedia": {
|
||||
"react-nativeGrantPermissions": "",
|
||||
"chromeGrantPermissions": "",
|
||||
"androidGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "",
|
||||
"operaGrantPermissions": "",
|
||||
"iexplorerGrantPermissions": "",
|
||||
"safariGrantPermissions": "",
|
||||
"nwjsGrantPermissions": ""
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"keyboardShortcuts": "",
|
||||
"raiseHand": "",
|
||||
"pushToTalk": "",
|
||||
"toggleScreensharing": "",
|
||||
"toggleFilmstrip": "",
|
||||
"toggleShortcuts": "",
|
||||
"focusLocal": "",
|
||||
"focusRemote": "",
|
||||
"toggleChat": "",
|
||||
"mute": "",
|
||||
"fullScreen": "",
|
||||
"videoMute": ""
|
||||
},
|
||||
"welcomepage": {
|
||||
"go": "",
|
||||
"roomname": "",
|
||||
"disable": "",
|
||||
"feature1": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature2": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature3": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature4": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature5": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature6": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature7": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature8": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
}
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
"title": ""
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"title": "",
|
||||
"rejoinKeyTitle": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "",
|
||||
"videomute": "",
|
||||
"authenticate": "",
|
||||
"lock": "",
|
||||
"invite": "",
|
||||
"chat": "",
|
||||
"etherpad": "",
|
||||
"sharedvideo": "",
|
||||
"sharescreen": "",
|
||||
"fullscreen": "",
|
||||
"sip": "",
|
||||
"Settings": "",
|
||||
"hangup": "",
|
||||
"login": "",
|
||||
"logout": "",
|
||||
"dialpad": "",
|
||||
"sharedVideoMutedPopup": "",
|
||||
"micMutedPopup": "",
|
||||
"talkWhileMutedPopup": "",
|
||||
"unableToUnmutePopup": "",
|
||||
"cameraDisabled": "",
|
||||
"micDisabled": "",
|
||||
"filmstrip": "",
|
||||
"profile": "",
|
||||
"raiseHand": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "",
|
||||
"filmstrip": "",
|
||||
"contactlist": ""
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "",
|
||||
"popover": ""
|
||||
},
|
||||
"messagebox": ""
|
||||
},
|
||||
"settings": {
|
||||
"title": "",
|
||||
"update": "",
|
||||
"name": "",
|
||||
"startAudioMuted": "",
|
||||
"startVideoMuted": "",
|
||||
"selectCamera": "",
|
||||
"selectMic": "",
|
||||
"selectAudioOutput": "",
|
||||
"followMe": "",
|
||||
"noDevice": "",
|
||||
"noPermission": "",
|
||||
"cameraAndMic": "",
|
||||
"moderator": "",
|
||||
"password": "",
|
||||
"audioVideo": "",
|
||||
"setPasswordLabel": ""
|
||||
},
|
||||
"profile": {
|
||||
"title": "",
|
||||
"setDisplayNameLabel": "",
|
||||
"setEmailLabel": "",
|
||||
"setEmailInput": ""
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "",
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"kick": "",
|
||||
"muted": "",
|
||||
"domute": "",
|
||||
"flip": ""
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "",
|
||||
"bitrate": "",
|
||||
"packetloss": "",
|
||||
"resolution": "",
|
||||
"less": "",
|
||||
"more": "",
|
||||
"address": "",
|
||||
"remoteport_plural": "",
|
||||
"remoteport": "",
|
||||
"localport_plural": "",
|
||||
"localport": "",
|
||||
"localaddress_plural": "",
|
||||
"localaddress": "",
|
||||
"remoteaddress_plural": "",
|
||||
"remoteaddress": "",
|
||||
"transport": "",
|
||||
"bandwidth": "",
|
||||
"na": ""
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": "",
|
||||
"muted": "",
|
||||
"mutedTitle": "",
|
||||
"raisedHand": ""
|
||||
},
|
||||
"dialog": {
|
||||
"add": "",
|
||||
"kickMessage": "",
|
||||
"popupError": "",
|
||||
"passwordErrorTitle": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"connectError": "",
|
||||
"connectErrorWithMsg": "",
|
||||
"incorrectPassword": "",
|
||||
"connecting": "",
|
||||
"copy": "",
|
||||
"error": "",
|
||||
"roomLocked": "",
|
||||
"addPassword": "",
|
||||
"createPassword": "",
|
||||
"detectext": "",
|
||||
"failtoinstall": "",
|
||||
"failedpermissions": "",
|
||||
"conferenceReloadTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"reconnectNow": "",
|
||||
"conferenceReloadTimeLeft": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupported": "",
|
||||
"internalErrorTitle": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"currentPassword": "",
|
||||
"passwordLabel": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"done": "",
|
||||
"Remove": "",
|
||||
"removePassword": "",
|
||||
"shareVideoTitle": "",
|
||||
"shareVideoLinkError": "",
|
||||
"removeSharedVideoTitle": "",
|
||||
"removeSharedVideoMsg": "",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"WaitingForHost": "",
|
||||
"WaitForHostMsg": "",
|
||||
"IamHost": "",
|
||||
"Cancel": "",
|
||||
"Submit": "",
|
||||
"retry": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"Save": "",
|
||||
"recording": "",
|
||||
"recordingToken": "",
|
||||
"Dial": "",
|
||||
"sipMsg": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"shareLink": "",
|
||||
"settings1": "",
|
||||
"settings2": "",
|
||||
"settings3": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": "",
|
||||
"tokenAuthFailedTitle": "",
|
||||
"tokenAuthFailed": "",
|
||||
"displayNameRequired": "",
|
||||
"enterDisplayName": "",
|
||||
"extensionRequired": "",
|
||||
"firefoxExtensionPrompt": "",
|
||||
"rateExperience": "",
|
||||
"feedbackHelp": "",
|
||||
"feedbackQuestion": "",
|
||||
"thankYou": "",
|
||||
"sorryFeedback": "",
|
||||
"liveStreaming": "",
|
||||
"streamKey": "",
|
||||
"startLiveStreaming": "",
|
||||
"stopStreamingWarning": "",
|
||||
"stopRecordingWarning": "",
|
||||
"stopLiveStreaming": "",
|
||||
"stopRecording": "",
|
||||
"doNotShowWarningAgain": "",
|
||||
"doNotShowMessageAgain": "",
|
||||
"permissionDenied": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"micErrorPresent": "",
|
||||
"cameraErrorPresent": "",
|
||||
"cameraUnsupportedResolutionError": "",
|
||||
"cameraUnknownError": "",
|
||||
"cameraPermissionDeniedError": "",
|
||||
"cameraNotFoundError": "",
|
||||
"cameraConstraintFailedError": "",
|
||||
"micUnknownError": "",
|
||||
"micPermissionDeniedError": "",
|
||||
"micNotFoundError": "",
|
||||
"micConstraintFailedError": "",
|
||||
"micNotSendingData": "",
|
||||
"cameraNotSendingData": "",
|
||||
"goToStore": "",
|
||||
"externalInstallationTitle": "",
|
||||
"externalInstallationMsg": "",
|
||||
"muteParticipantTitle": "",
|
||||
"muteParticipantBody": "",
|
||||
"muteParticipantButton": ""
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"and": ""
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "",
|
||||
"CONNECTING": "",
|
||||
"RECONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ATTACHED": ""
|
||||
},
|
||||
"recording": {
|
||||
"pending": "",
|
||||
"on": "",
|
||||
"off": "",
|
||||
"failedToStart": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"unavailable": ""
|
||||
},
|
||||
"liveStreaming": {
|
||||
"pending": "",
|
||||
"on": "",
|
||||
"off": "",
|
||||
"unavailable": "",
|
||||
"failedToStart": "",
|
||||
"buttonTooltip": "",
|
||||
"streamIdRequired": "",
|
||||
"error": "",
|
||||
"busy": ""
|
||||
}
|
||||
}
|
||||
@@ -387,6 +387,7 @@
|
||||
"failedToStart": "Live streaming failed to start",
|
||||
"buttonTooltip": "Start / Stop live stream",
|
||||
"streamIdRequired": "Please fill in the stream id in order to launch the live streaming.",
|
||||
"streamIdHelp": "Where do I find this?",
|
||||
"error": "Live streaming failed. Please try again.",
|
||||
"busy": "All recorders are currently busy. Please try again later."
|
||||
}
|
||||
|
||||
@@ -712,6 +712,17 @@ UI.inputDisplayNameHandler = function (newDisplayName) {
|
||||
eventEmitter.emit(UIEvents.NICKNAME_CHANGED, newDisplayName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show custom popup/tooltip for a specified button.
|
||||
* @param popupSelectorID the selector id of the popup to show
|
||||
* @param show true or false/show or hide the popup
|
||||
* @param timeout the time to show the popup
|
||||
*/
|
||||
UI.showCustomToolbarPopup = function (popupSelectorID, show, timeout) {
|
||||
eventEmitter.emit(UIEvents.SHOW_CUSTOM_TOOLBAR_BUTTON_POPUP,
|
||||
popupSelectorID, show, timeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the type of the remote video.
|
||||
* @param jid the jid for the remote video
|
||||
|
||||
@@ -51,6 +51,9 @@ function _requestLiveStreamId() {
|
||||
const streamIdRequired
|
||||
= APP.translation.generateTranslationHTML(
|
||||
"liveStreaming.streamIdRequired");
|
||||
const streamIdHelp
|
||||
= APP.translation.generateTranslationHTML(
|
||||
"liveStreaming.streamIdHelp");
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openDialogWithStates({
|
||||
@@ -60,7 +63,11 @@ function _requestLiveStreamId() {
|
||||
`<input class="input-control"
|
||||
name="streamId" type="text"
|
||||
data-i18n="[placeholder]dialog.streamKey"
|
||||
autofocus>`,
|
||||
autofocus><div style="text-align: right">
|
||||
<a class="helper-link" target="_new"
|
||||
href="${interfaceConfig.LIVE_STREAMING_HELP_LINK}">`
|
||||
+ streamIdHelp
|
||||
+ `</a></div>`,
|
||||
persistent: false,
|
||||
buttons: [
|
||||
{title: cancelButton, value: false},
|
||||
|
||||
@@ -541,7 +541,7 @@ export default class SharedVideoManager {
|
||||
if(show)
|
||||
this.showSharedVideoMutedPopup(false);
|
||||
|
||||
UIUtil.animateShowElement($("#micMutedPopup"), show, 5000);
|
||||
APP.UI.showCustomToolbarPopup('#micMutedPopup', show, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -554,7 +554,7 @@ export default class SharedVideoManager {
|
||||
if(show)
|
||||
this.showMicMutedPopup(false);
|
||||
|
||||
UIUtil.animateShowElement($("#sharedVideoMutedPopup"), show, 5000);
|
||||
APP.UI.showCustomToolbarPopup('#sharedVideoMutedPopup', show, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global APP, $, config, interfaceConfig, JitsiMeetJS */
|
||||
/* global AJS, APP, $, config, interfaceConfig, JitsiMeetJS */
|
||||
import UIUtil from '../util/UIUtil';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import SideContainerToggler from "../side_pannels/SideContainerToggler";
|
||||
@@ -26,8 +26,8 @@ const buttonHandlers = {
|
||||
if (sharedVideoManager
|
||||
&& sharedVideoManager.isSharedVideoVolumeOn()
|
||||
&& !sharedVideoManager.isSharedVideoOwner()) {
|
||||
UIUtil.animateShowElement(
|
||||
$("#unableToUnmutePopup"), true, 5000);
|
||||
APP.UI.showCustomToolbarPopup(
|
||||
'#unableToUnmutePopup', true, 5000);
|
||||
}
|
||||
else {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.audio.unmuted');
|
||||
@@ -120,19 +120,19 @@ const defaultToolbarButtons = {
|
||||
shortcutDescription: "keyboardShortcuts.mute",
|
||||
popups: [
|
||||
{
|
||||
id: "micMutedPopup",
|
||||
className: "loginmenu",
|
||||
dataAttr: "[html]toolbar.micMutedPopup"
|
||||
id: 'micMutedPopup',
|
||||
className: 'loginmenu',
|
||||
dataAttr: '[title]toolbar.micMutedPopup'
|
||||
},
|
||||
{
|
||||
id: "unableToUnmutePopup",
|
||||
className: "loginmenu",
|
||||
dataAttr: "[html]toolbar.unableToUnmutePopup"
|
||||
id: 'unableToUnmutePopup',
|
||||
className: 'loginmenu',
|
||||
dataAttr: '[title]toolbar.unableToUnmutePopup'
|
||||
},
|
||||
{
|
||||
id: "talkWhileMutedPopup",
|
||||
className: "loginmenu",
|
||||
dataAttr: "[html]toolbar.talkWhileMutedPopup"
|
||||
id: 'talkWhileMutedPopup',
|
||||
className: 'loginmenu',
|
||||
dataAttr: '[title]toolbar.talkWhileMutedPopup'
|
||||
}
|
||||
],
|
||||
content: "Mute / Unmute",
|
||||
@@ -263,11 +263,14 @@ const defaultToolbarButtons = {
|
||||
id: 'toolbar_button_sharedvideo',
|
||||
tooltipKey: 'toolbar.sharedvideo',
|
||||
className: 'button icon-shared-video',
|
||||
html: `<ul id="sharedVideoMutedPopup"
|
||||
class="loginmenu extendedToolbarPopup">
|
||||
<li data-i18n="[html]toolbar.sharedVideoMutedPopup"></li>
|
||||
</ul>
|
||||
`
|
||||
popups: [
|
||||
{
|
||||
id: 'sharedVideoMutedPopup',
|
||||
className: 'loginmenu extendedToolbarPopup',
|
||||
dataAttr: '[title]toolbar.sharedVideoMutedPopup',
|
||||
dataAttrPosition: 'w'
|
||||
}
|
||||
]
|
||||
},
|
||||
'sip': {
|
||||
id: 'toolbar_button_sip',
|
||||
@@ -358,6 +361,11 @@ Toolbar = {
|
||||
Toolbar._handleFullScreenToggled(isFullScreen);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.SHOW_CUSTOM_TOOLBAR_BUTTON_POPUP,
|
||||
(popupID, show, timeout) => {
|
||||
Toolbar._showCustomToolbarPopup(popupID, show, timeout);
|
||||
});
|
||||
|
||||
if(!APP.tokenData.isGuest) {
|
||||
$("#toolbar_button_profile").addClass("unclickable");
|
||||
UIUtil.removeTooltip(
|
||||
@@ -724,17 +732,52 @@ Toolbar = {
|
||||
|
||||
_addPopups(buttonElement, popups = []) {
|
||||
popups.forEach((popup) => {
|
||||
let popupElement = document.createElement("ul");
|
||||
const popupElement = document.createElement('div');
|
||||
popupElement.id = popup.id;
|
||||
popupElement.className = popup.className;
|
||||
let liElement = document.createElement("li");
|
||||
liElement.setAttribute("data-i18n", popup.dataAttr);
|
||||
popupElement.appendChild(liElement);
|
||||
popupElement.setAttribute('data-i18n', popup.dataAttr);
|
||||
|
||||
let gravity = 'n';
|
||||
if (popup.dataAttrPosition)
|
||||
gravity = popup.dataAttrPosition;
|
||||
// use custom attribute to save gravity option
|
||||
// we use 'data-tooltip' in UIUtil to activate all tooltips
|
||||
// but we want these to be manually triggered
|
||||
popupElement.setAttribute('tooltip-gravity', gravity);
|
||||
|
||||
APP.translation.translateElement($(popupElement));
|
||||
|
||||
buttonElement.appendChild(popupElement);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show custom popup/tooltip for a specified button.
|
||||
* @param popupSelectorID the selector id of the popup to show
|
||||
* @param show true or false/show or hide the popup
|
||||
* @param timeout the time to show the popup
|
||||
*/
|
||||
_showCustomToolbarPopup(popupSelectorID, show, timeout) {
|
||||
|
||||
const gravity = $(popupSelectorID).attr('tooltip-gravity');
|
||||
AJS.$(popupSelectorID)
|
||||
.tooltip({
|
||||
trigger: 'manual',
|
||||
html: true,
|
||||
gravity: gravity,
|
||||
title: 'title'});
|
||||
if (show) {
|
||||
AJS.$(popupSelectorID).tooltip('show');
|
||||
setTimeout(function () {
|
||||
// hide the tooltip
|
||||
AJS.$(popupSelectorID).tooltip('hide');
|
||||
}, timeout);
|
||||
} else {
|
||||
AJS.$(popupSelectorID).tooltip('hide');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the toggled state of the given element depending on the isToggled
|
||||
* parameter.
|
||||
*
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"react": "15.4.2",
|
||||
"react-dom": "15.4.2",
|
||||
"react-native": "0.41.2",
|
||||
"react-native-background-timer": "1.0.0",
|
||||
"react-native-immersive": "0.0.4",
|
||||
"react-native-keep-awake": "^2.0.2",
|
||||
"react-native-prompt": "^1.0.0",
|
||||
@@ -66,7 +67,7 @@
|
||||
"eslint-plugin-jsdoc": "*",
|
||||
"eslint-plugin-react": "*",
|
||||
"eslint-plugin-react-native": "^2.2.1",
|
||||
"expose-loader": "*",
|
||||
"expose-loader": "0.7.1",
|
||||
"file-loader": "^0.10.0",
|
||||
"flow-bin": "^0.37.0",
|
||||
"haste-resolver-webpack-plugin": "^0.2.2",
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
_parseURIString,
|
||||
init
|
||||
} from './functions';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Temporary solution. Should dispatch actions related to initial settings of
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
import { Linking } from 'react-native';
|
||||
|
||||
import { Platform } from '../../base/react';
|
||||
import '../../audio-mode';
|
||||
import '../../background';
|
||||
import { Platform } from '../../base/react';
|
||||
import '../../full-screen';
|
||||
import '../../wake-lock';
|
||||
|
||||
|
||||
@@ -2,3 +2,5 @@ export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './components';
|
||||
export * from './functions';
|
||||
|
||||
import './reducer';
|
||||
|
||||
43
react/features/background/actionTypes.js
Normal file
43
react/features/background/actionTypes.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Symbol } from '../base/react';
|
||||
|
||||
/**
|
||||
* The type of redux action to set the AppState API change event listener.
|
||||
*
|
||||
* {
|
||||
* type: _SET_APP_STATE_LISTENER,
|
||||
* listener: Function
|
||||
* }
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
export const _SET_APP_STATE_LISTENER
|
||||
= Symbol('_SET_APP_STATE_LISTENER');
|
||||
|
||||
/**
|
||||
* The type of redux action which signals that video will be muted because the
|
||||
* app is going to the background.
|
||||
*
|
||||
* {
|
||||
* type: _SET_BACKGROUND_VIDEO_MUTED,
|
||||
* muted: boolean
|
||||
* }
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
export const _SET_BACKGROUND_VIDEO_MUTED
|
||||
= Symbol('_SET_BACKGROUND_VIDEO_MUTED');
|
||||
|
||||
/**
|
||||
* The type of redux action which signals that the app state has changed (in
|
||||
* terms of execution mode). The app state can be one of 'active', 'inactive',
|
||||
* or 'background'.
|
||||
*
|
||||
* {
|
||||
* type: APP_STATE_CHANGED,
|
||||
* appState: string
|
||||
* }
|
||||
*
|
||||
* @public
|
||||
* @see {@link https://facebook.github.io/react-native/docs/appstate.html}
|
||||
*/
|
||||
export const APP_STATE_CHANGED = Symbol('APP_STATE_CHANGED');
|
||||
81
react/features/background/actions.js
Normal file
81
react/features/background/actions.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { setVideoMuted } from '../base/media';
|
||||
|
||||
import {
|
||||
_SET_APP_STATE_LISTENER,
|
||||
_SET_BACKGROUND_VIDEO_MUTED,
|
||||
APP_STATE_CHANGED
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Signals that the App state has changed (in terms of execution state). The
|
||||
* application can be in 3 states: 'active', 'inactive' and 'background'.
|
||||
*
|
||||
* @param {string} appState - The new App state.
|
||||
* @public
|
||||
* @returns {{
|
||||
* type: APP_STATE_CHANGED,
|
||||
* appState: string
|
||||
* }}
|
||||
* @see {@link https://facebook.github.io/react-native/docs/appstate.html}
|
||||
*/
|
||||
export function appStateChanged(appState: string) {
|
||||
return {
|
||||
type: APP_STATE_CHANGED,
|
||||
appState
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to be used with React Native's AppState API.
|
||||
*
|
||||
* @param {Function} listener - Function to be set as the change event listener.
|
||||
* @protected
|
||||
* @returns {{
|
||||
* type: _SET_APP_STATE_LISTENER,
|
||||
* listener: Function
|
||||
* }}
|
||||
*/
|
||||
export function _setAppStateListener(listener: ?Function) {
|
||||
return {
|
||||
type: _SET_APP_STATE_LISTENER,
|
||||
listener
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that the app should mute video because it's now running in the
|
||||
* background, or unmute it because it came back from the background. If video
|
||||
* was already muted nothing will happen; otherwise, it will be muted. When
|
||||
* coming back from the background the previous state will be restored.
|
||||
*
|
||||
* @param {boolean} muted - True if video should be muted; false, otherwise.
|
||||
* @protected
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function _setBackgroundVideoMuted(muted: boolean) {
|
||||
return (dispatch, getState) => {
|
||||
if (muted) {
|
||||
const mediaState = getState()['features/base/media'];
|
||||
|
||||
if (mediaState.video.muted) {
|
||||
// Video is already muted, do nothing.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const bgState = getState()['features/background'];
|
||||
|
||||
if (!bgState.videoMuted) {
|
||||
// We didn't mute video, do nothing.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Remember that video was muted due to the app going to the background
|
||||
// vs user's choice.
|
||||
dispatch({
|
||||
type: _SET_BACKGROUND_VIDEO_MUTED,
|
||||
muted
|
||||
});
|
||||
dispatch(setVideoMuted(muted));
|
||||
};
|
||||
}
|
||||
5
react/features/background/index.js
Normal file
5
react/features/background/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
109
react/features/background/middleware.js
Normal file
109
react/features/background/middleware.js
Normal file
@@ -0,0 +1,109 @@
|
||||
/* @flow */
|
||||
|
||||
import { AppState } from 'react-native';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
APP_WILL_MOUNT,
|
||||
APP_WILL_UNMOUNT
|
||||
} from '../app';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
_setAppStateListener,
|
||||
_setBackgroundVideoMuted,
|
||||
appStateChanged
|
||||
} from './actions';
|
||||
import {
|
||||
_SET_APP_STATE_LISTENER,
|
||||
APP_STATE_CHANGED
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Middleware that captures App lifetime actions and subscribes to application
|
||||
* state changes. When the application state changes it will fire the action
|
||||
* required to mute or unmute the local video in case the application goes to
|
||||
* the background or comes back from it.
|
||||
*
|
||||
* @param {Store} store - Redux store.
|
||||
* @returns {Function}
|
||||
* @see {@link https://facebook.github.io/react-native/docs/appstate.html}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case _SET_APP_STATE_LISTENER: {
|
||||
// Remove the current/old AppState listener.
|
||||
const { appStateListener } = store.getState()['features/background'];
|
||||
|
||||
if (appStateListener) {
|
||||
AppState.removeEventListener('change', appStateListener);
|
||||
}
|
||||
|
||||
// Add the new AppState listener.
|
||||
if (action.listener) {
|
||||
AppState.addEventListener('change', action.listener);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case APP_STATE_CHANGED:
|
||||
_appStateChanged(store.dispatch, action.appState);
|
||||
break;
|
||||
|
||||
case APP_WILL_MOUNT:
|
||||
store.dispatch(
|
||||
_setAppStateListener(
|
||||
_onAppStateChange.bind(undefined, store.dispatch)));
|
||||
break;
|
||||
|
||||
case APP_WILL_UNMOUNT:
|
||||
store.dispatch(_setAppStateListener(null));
|
||||
break;
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles app state changes. Dispatches the necessary Redux actions for the
|
||||
* local video to be muted when the app goes to the background, and to be
|
||||
* unmuted when the app comes back.
|
||||
*
|
||||
* @param {Dispatch} dispatch - Redux dispatch function.
|
||||
* @param {string} appState - The current app state.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _appStateChanged(dispatch: Dispatch<*>, appState: string) {
|
||||
let muted;
|
||||
|
||||
switch (appState) {
|
||||
case 'active':
|
||||
muted = false;
|
||||
break;
|
||||
|
||||
case 'background':
|
||||
muted = true;
|
||||
break;
|
||||
|
||||
case 'inactive':
|
||||
default:
|
||||
// XXX: We purposely don't handle the 'inactive' app state.
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(_setBackgroundVideoMuted(muted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by React Native's AppState API to notify that the application state
|
||||
* has changed. Dispatches the change within the (associated) Redux store.
|
||||
*
|
||||
* @param {Dispatch} dispatch - Redux dispatch function.
|
||||
* @param {string} appState - The current application execution state.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onAppStateChange(dispatch: Dispatch<*>, appState: string) {
|
||||
dispatch(appStateChanged(appState));
|
||||
}
|
||||
31
react/features/background/reducer.js
Normal file
31
react/features/background/reducer.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
_SET_APP_STATE_LISTENER,
|
||||
_SET_BACKGROUND_VIDEO_MUTED,
|
||||
APP_STATE_CHANGED
|
||||
} from './actionTypes';
|
||||
|
||||
ReducerRegistry.register('features/background', (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case _SET_APP_STATE_LISTENER:
|
||||
return {
|
||||
...state,
|
||||
appStateListener: action.listener
|
||||
};
|
||||
|
||||
case _SET_BACKGROUND_VIDEO_MUTED:
|
||||
return {
|
||||
...state,
|
||||
videoMuted: action.muted
|
||||
};
|
||||
|
||||
case APP_STATE_CHANGED:
|
||||
return {
|
||||
...state,
|
||||
appState: action.appState
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
@@ -20,8 +20,6 @@ import {
|
||||
} from './actionTypes';
|
||||
import { EMAIL_COMMAND } from './constants';
|
||||
import { _addLocalTracksToConference } from './functions';
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Adds conference (event) listeners.
|
||||
|
||||
@@ -19,8 +19,8 @@ export function _addLocalTracksToConference(conference, localTracks) {
|
||||
// XXX The library lib-jitsi-meet may be draconian, for example, when
|
||||
// adding one and the same video track multiple times.
|
||||
if (conferenceLocalTracks.indexOf(track) === -1) {
|
||||
promises.push(conference.addTrack(track)
|
||||
.catch(err => {
|
||||
promises.push(
|
||||
conference.addTrack(track).catch(err => {
|
||||
_reportError(
|
||||
'Failed to add local track to conference',
|
||||
err);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
CONNECTION_FAILED,
|
||||
SET_DOMAIN
|
||||
} from './actionTypes';
|
||||
import './reducer';
|
||||
|
||||
const JitsiConnectionEvents = JitsiMeetJS.events.connection;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { Dispatch } from 'redux';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import { SET_DOMAIN } from './actionTypes';
|
||||
import './reducer';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var JitsiMeetJS: Object;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './functions';
|
||||
|
||||
import './reducer';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* global JitsiMeetJS */
|
||||
/* @flow */
|
||||
|
||||
declare var JitsiMeetJS: Object;
|
||||
|
||||
export default JitsiMeetJS;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import JitsiMeetJS from './';
|
||||
import {
|
||||
LIB_DISPOSED,
|
||||
@@ -5,8 +9,8 @@ import {
|
||||
LIB_INITIALIZED,
|
||||
SET_CONFIG
|
||||
} from './actionTypes';
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Disposes lib-jitsi-meet.
|
||||
@@ -20,7 +24,7 @@ export function disposeLib() {
|
||||
// there is a big chance it will be async.
|
||||
// TODO Currently, lib-jitsi-meet doesn't have any functionality to
|
||||
// dispose itself.
|
||||
return dispatch => {
|
||||
return (dispatch: Dispatch<*>) => {
|
||||
dispatch({ type: LIB_DISPOSED });
|
||||
|
||||
return Promise.resolve();
|
||||
@@ -33,7 +37,7 @@ export function disposeLib() {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function initLib() {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch: Dispatch<*>, getState: Function) => {
|
||||
const config = getState()['features/base/lib-jitsi-meet'].config;
|
||||
|
||||
if (!config) {
|
||||
@@ -51,7 +55,7 @@ export function initLib() {
|
||||
.catch(error => {
|
||||
dispatch({
|
||||
type: LIB_INIT_ERROR,
|
||||
lib: { error }
|
||||
error
|
||||
});
|
||||
|
||||
// TODO Handle LIB_INIT_ERROR error somewhere instead.
|
||||
@@ -67,11 +71,11 @@ export function initLib() {
|
||||
* @param {Object} config - Config object accepted by JitsiMeetJS#init()
|
||||
* method.
|
||||
* @returns {{
|
||||
* type: SET_CONFIG,
|
||||
* config: Object
|
||||
* }}
|
||||
* type: SET_CONFIG,
|
||||
* config: Object
|
||||
* }}
|
||||
*/
|
||||
export function setConfig(config) {
|
||||
export function setConfig(config: Object) {
|
||||
return {
|
||||
type: SET_CONFIG,
|
||||
config
|
||||
|
||||
@@ -6,3 +6,6 @@ export { JitsiMeetJS as default };
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -22,24 +22,44 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
action.participant.local && store.dispatch(disposeLib());
|
||||
break;
|
||||
|
||||
case SET_CONFIG: {
|
||||
const { dispatch, getState } = store;
|
||||
const initialized
|
||||
= getState()['features/base/lib-jitsi-meet'].initialized;
|
||||
|
||||
// XXX If we already have config, that means new config is coming, which
|
||||
// means that we should dispose instance of lib initialized with
|
||||
// previous config first.
|
||||
// TODO Currently 'disposeLib' actually does not dispose lib-jitsi-meet.
|
||||
// This functionality should be implemented.
|
||||
const promise
|
||||
= initialized ? dispatch(disposeLib()) : Promise.resolve();
|
||||
|
||||
promise.then(dispatch(initLib()));
|
||||
|
||||
break;
|
||||
}
|
||||
case SET_CONFIG:
|
||||
return _setConfig(store, next, action);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Notifies the feature base/lib-jitsi-meet that the action SET_CONFIG is being
|
||||
* dispatched within a specific Redux store.
|
||||
*
|
||||
* @param {Store} store - The Redux store in which the specified action is being
|
||||
* dispatched.
|
||||
* @param {Dispatch} next - The Redux dispatch function to dispatch the
|
||||
* specified action to the specified store.
|
||||
* @param {Action} action - The Redux action SET_CONFIG which is being
|
||||
* dispatched in the specified store.
|
||||
* @private
|
||||
* @returns {Object} The new state that is the result of the reduction of the
|
||||
* specified action.
|
||||
*/
|
||||
function _setConfig(store, next, action) {
|
||||
const { dispatch, getState } = store;
|
||||
const { initialized } = getState()['features/base/lib-jitsi-meet'];
|
||||
|
||||
// XXX Since the config is changing, the library lib-jitsi-meet must be
|
||||
// initialized again with the new config. Consequntly, it may need to be
|
||||
// disposed of first.
|
||||
// TODO Currently, disposeLib actually does not dispose of lib-jitsi-meet
|
||||
// because lib-jitsi-meet does not implement such functionality.
|
||||
const disposeLIbPromise
|
||||
= initialized ? dispatch(disposeLib()) : Promise.resolve();
|
||||
|
||||
// Let the new config into the Redux store (because initLib will read it
|
||||
// from there).
|
||||
const nextState = next(action);
|
||||
|
||||
disposeLIbPromise.then(dispatch(initLib()));
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
require('./polyfills-browser');
|
||||
require('./polyfills-browserify');
|
||||
import './polyfills-browser';
|
||||
import './polyfills-browserify';
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import BackgroundTimer from 'react-native-background-timer';
|
||||
import 'url-polyfill'; // Polyfill for URL constructor
|
||||
|
||||
/**
|
||||
* Gets the first common prototype of two specified Objects (treating the
|
||||
* objects themselves as prototypes as well).
|
||||
@@ -84,10 +87,6 @@ function _visitNode(node, callback) {
|
||||
}
|
||||
|
||||
(global => {
|
||||
|
||||
// Polyfill for URL constructor
|
||||
require('url-polyfill');
|
||||
|
||||
const DOMParser = require('xmldom').DOMParser;
|
||||
|
||||
// addEventListener
|
||||
@@ -108,8 +107,8 @@ function _visitNode(node, callback) {
|
||||
if (typeof global.document === 'undefined') {
|
||||
const document
|
||||
= new DOMParser().parseFromString(
|
||||
/* source */ '<html><head></head><body></body></html>',
|
||||
/* mineType */ 'text/xml');
|
||||
'<html><head></head><body></body></html>',
|
||||
'text/xml');
|
||||
|
||||
// document.addEventListener
|
||||
//
|
||||
@@ -141,11 +140,49 @@ function _visitNode(node, callback) {
|
||||
const elementPrototype
|
||||
= Object.getPrototypeOf(document.documentElement);
|
||||
|
||||
if (elementPrototype
|
||||
&& typeof elementPrototype.querySelector === 'undefined') {
|
||||
elementPrototype.querySelector = function(selectors) {
|
||||
return _querySelector(this, selectors);
|
||||
};
|
||||
if (elementPrototype) {
|
||||
if (typeof elementPrototype.querySelector === 'undefined') {
|
||||
elementPrototype.querySelector = function(selectors) {
|
||||
return _querySelector(this, selectors);
|
||||
};
|
||||
}
|
||||
|
||||
// Element.innerHTML
|
||||
//
|
||||
// Required by:
|
||||
// - jQuery's .append method
|
||||
if (!elementPrototype.hasOwnProperty('innerHTML')) {
|
||||
Object.defineProperty(elementPrototype, 'innerHTML', {
|
||||
get() {
|
||||
return this.childNodes.toString();
|
||||
},
|
||||
|
||||
set(innerHTML) {
|
||||
// MDN says: removes all of element's children, parses
|
||||
// the content string and assigns the resulting nodes as
|
||||
// children of the element.
|
||||
|
||||
// Remove all of element's children.
|
||||
this.textContent = '';
|
||||
|
||||
// Parse the content string.
|
||||
const d
|
||||
= new DOMParser().parseFromString(
|
||||
`<div>${innerHTML}</div>`,
|
||||
'text/xml');
|
||||
|
||||
// Assign the resulting nodes as children of the
|
||||
// element.
|
||||
const documentElement = d.documentElement;
|
||||
let child;
|
||||
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while (child = documentElement.firstChild) {
|
||||
this.appendChild(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME There is a weird infinite loop related to console.log and
|
||||
@@ -294,17 +331,17 @@ function _visitNode(node, callback) {
|
||||
//
|
||||
// Required by:
|
||||
// - Strophe
|
||||
if (prototype && typeof prototype.responseXML === 'undefined') {
|
||||
if (prototype && !prototype.hasOwnProperty('responseXML')) {
|
||||
Object.defineProperty(prototype, 'responseXML', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
const responseText = this.responseText;
|
||||
let responseXML;
|
||||
|
||||
if (responseText) {
|
||||
responseXML = new DOMParser()
|
||||
.parseFromString(responseText);
|
||||
responseXML
|
||||
= new DOMParser().parseFromString(
|
||||
responseText,
|
||||
'text/xml');
|
||||
}
|
||||
|
||||
return responseXML;
|
||||
@@ -313,4 +350,18 @@ function _visitNode(node, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
// Timers
|
||||
//
|
||||
// React Native's timers won't run while the app is in the background, this
|
||||
// is a known limitation. Replace them with a background-friendly
|
||||
// alternative.
|
||||
//
|
||||
// Required by:
|
||||
// - lib-jitsi-meet
|
||||
// - Strophe
|
||||
global.clearTimeout = window.clearTimeout = BackgroundTimer.clearTimeout;
|
||||
global.clearInterval = window.clearInterval = BackgroundTimer.clearInterval;
|
||||
global.setInterval = window.setInterval = BackgroundTimer.setInterval;
|
||||
global.setTimeout = window.setTimeout = BackgroundTimer.setTimeout;
|
||||
|
||||
})(global || window || this); // eslint-disable-line no-invalid-this
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Initial state of 'features/base/lib-jitsi-meet'.
|
||||
* The initial state of 'features/base/lib-jitsi-meet'.
|
||||
*
|
||||
* @type {{
|
||||
* initializationError: null,
|
||||
@@ -16,6 +16,14 @@ import {
|
||||
* }}
|
||||
*/
|
||||
const INITIAL_STATE = {
|
||||
/**
|
||||
* The mandatory configuration to be passed to JitsiMeetJS#init(). The app
|
||||
* will download config.js from the Jitsi Meet deployment and taks its
|
||||
* values into account but the values bellow will be enforced (because they
|
||||
* are essential to the correct execution of the application).
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
config: {
|
||||
// FIXME The support for audio levels in lib-jitsi-meet polls the
|
||||
// statistics of WebRTC at a short interval multiple times a second.
|
||||
@@ -47,7 +55,7 @@ ReducerRegistry.register(
|
||||
case LIB_INIT_ERROR:
|
||||
return {
|
||||
...state,
|
||||
initializationError: action.lib.error,
|
||||
initializationError: action.error,
|
||||
initialized: false
|
||||
};
|
||||
|
||||
@@ -80,10 +88,12 @@ function _setConfig(state, action) {
|
||||
return {
|
||||
...state,
|
||||
config: {
|
||||
// The final config is the result of augmenting the default config
|
||||
// with whatever the deployment has chosen to override/overwrite.
|
||||
...INITIAL_STATE.config,
|
||||
...action.config
|
||||
...action.config,
|
||||
|
||||
// The config of INITIAL_STATE is meant to override the config
|
||||
// downloaded from the Jitsi Meet deployment because the former
|
||||
// contains values that are mandatory.
|
||||
...INITIAL_STATE.config
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
SET_VIDEO_MUTED
|
||||
} from './actionTypes';
|
||||
import { CAMERA_FACING_MODE } from './constants';
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Action to set the muted state of the local audio.
|
||||
|
||||
@@ -3,3 +3,6 @@ export * from './actionTypes';
|
||||
export * from './components';
|
||||
export * from './constants';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -7,8 +7,6 @@ import {
|
||||
PIN_PARTICIPANT
|
||||
} from './actionTypes';
|
||||
import { getLocalParticipant } from './functions';
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Action to update a participant's email.
|
||||
|
||||
@@ -2,3 +2,6 @@ export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './constants';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { shimStyles } from './shimStyles';
|
||||
|
||||
/**
|
||||
* Create a style sheet using the provided style definitions.
|
||||
*
|
||||
* @param {Object} styles - A dictionary of named style definitions.
|
||||
* @param {Object} [overrides={}] - Optional set of additional (often
|
||||
* platform-dependent/specific) style definitions that will override the base
|
||||
* (often platform-independent) styles.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function createStyleSheet(styles, overrides = {}) {
|
||||
const combinedStyles = {};
|
||||
|
||||
for (const k of Object.keys(styles)) {
|
||||
combinedStyles[k]
|
||||
= shimStyles({
|
||||
...styles[k],
|
||||
...overrides[k]
|
||||
});
|
||||
}
|
||||
|
||||
return combinedStyles;
|
||||
}
|
||||
96
react/features/base/styles/functions.js
Normal file
96
react/features/base/styles/functions.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/* @flow */
|
||||
|
||||
import { Platform } from '../react';
|
||||
|
||||
import { ColorPalette } from './components';
|
||||
|
||||
declare type StyleSheet = Object;
|
||||
|
||||
/**
|
||||
* The list of the well-known style properties which may not be numbers on Web
|
||||
* but must be numbers on React Native.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const _WELL_KNOWN_NUMBER_PROPERTIES = [ 'height', 'width' ];
|
||||
|
||||
/* eslint-disable flowtype/space-before-type-colon */
|
||||
|
||||
/**
|
||||
* Create a style sheet using the provided style definitions.
|
||||
*
|
||||
* @param {StyleSheet} styles - A dictionary of named style definitions.
|
||||
* @param {StyleSheet} [overrides={}] - Optional set of additional (often
|
||||
* platform-dependent/specific) style definitions that will override the base
|
||||
* (often platform-independent) styles.
|
||||
* @returns {StyleSheet}
|
||||
*/
|
||||
export function createStyleSheet(styles: StyleSheet, overrides: StyleSheet = {})
|
||||
: StyleSheet {
|
||||
|
||||
/* eslint-enable flowtype/space-before-type-colon */
|
||||
|
||||
const combinedStyles = {};
|
||||
|
||||
for (const k of Object.keys(styles)) {
|
||||
combinedStyles[k]
|
||||
= _shimStyles({
|
||||
...styles[k],
|
||||
...overrides[k]
|
||||
});
|
||||
}
|
||||
|
||||
return combinedStyles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Works around a bug in react-native or react-native-webrtc on Android which
|
||||
* causes Views overlaying RTCView to be clipped. Even though we (may) display
|
||||
* multiple RTCViews, it is enough to apply the fix only to a View with a
|
||||
* bounding rectangle containing all RTCviews and their overlaying Views.
|
||||
*
|
||||
* @param {StyleSheet} styles - An object which represents a stylesheet.
|
||||
* @public
|
||||
* @returns {StyleSheet}
|
||||
*/
|
||||
export function fixAndroidViewClipping<T: StyleSheet>(styles: T): T {
|
||||
if (Platform.OS === 'android') {
|
||||
styles.borderColor = ColorPalette.appBackground;
|
||||
styles.borderWidth = 0.2;
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shims style properties to work correctly on native. Allows us to minimize the
|
||||
* number of style declarations that need to be set or overridden for specific
|
||||
* platforms.
|
||||
*
|
||||
* @param {StyleSheet} styles - An object which represents a stylesheet.
|
||||
* @private
|
||||
* @returns {StyleSheet}
|
||||
*/
|
||||
function _shimStyles<T: StyleSheet>(styles: T): T {
|
||||
// Certain style properties may not be numbers on Web but must be numbers on
|
||||
// React Native. For example, height and width may be expressed in percent
|
||||
// on Web but React Native will not understand them and we will get errors
|
||||
// (at least during development). Convert such well-known properties to
|
||||
// numbers if possible; otherwise, remove them to avoid runtime errors.
|
||||
for (const k of _WELL_KNOWN_NUMBER_PROPERTIES) {
|
||||
const v = styles[k];
|
||||
const typeofV = typeof v;
|
||||
|
||||
if (typeofV !== 'undefined' && typeofV !== 'number') {
|
||||
const numberV = Number(v);
|
||||
|
||||
if (Number.isNaN(numberV)) {
|
||||
delete styles[k];
|
||||
} else {
|
||||
styles[k] = numberV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './components';
|
||||
export * from './createStyleSheet';
|
||||
export * from './functions';
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* The list of the well-known style properties which may not be numbers on Web
|
||||
* but must be numbers on React Native.
|
||||
*/
|
||||
const WELL_KNOWN_NUMBER_PROPERTIES = [ 'height', 'width' ];
|
||||
|
||||
/**
|
||||
* Shim style properties to work correctly on native.
|
||||
*
|
||||
* Using this shimStyles method allows us to minimize the number of style
|
||||
* declarations that need to be set or overridden for specific platforms.
|
||||
*
|
||||
* @param {Object} styles - A dictionary of named style definitions.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function shimStyles(styles) {
|
||||
// Certain style properties may not be numbers on Web but must be numbers on
|
||||
// React Native. For example, height and width may be expressed in percent
|
||||
// on Web but React Native will not understand them and we will get errors
|
||||
// (at least during development). Convert such well-known properties to
|
||||
// numbers if possible; otherwise, remove them to avoid runtime errors.
|
||||
for (const k of WELL_KNOWN_NUMBER_PROPERTIES) {
|
||||
const v = styles[k];
|
||||
const typeofV = typeof v;
|
||||
|
||||
if (typeofV !== 'undefined' && typeofV !== 'number') {
|
||||
const numberV = Number(v);
|
||||
|
||||
if (Number.isNaN(numberV)) {
|
||||
delete styles[k];
|
||||
} else {
|
||||
styles[k] = numberV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import {
|
||||
TRACK_REMOVED,
|
||||
TRACK_UPDATED
|
||||
} from './actionTypes';
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
const JitsiTrackErrors = JitsiMeetJS.errors.track;
|
||||
const JitsiTrackEvents = JitsiMeetJS.events.track;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ColorPalette, createStyleSheet } from '../../base/styles';
|
||||
import {
|
||||
ColorPalette,
|
||||
createStyleSheet,
|
||||
fixAndroidViewClipping
|
||||
} from '../../base/styles';
|
||||
|
||||
/**
|
||||
* The style of the conference UI (component).
|
||||
@@ -15,11 +19,11 @@ export const styles = createStyleSheet({
|
||||
/**
|
||||
* Conference style.
|
||||
*/
|
||||
conference: {
|
||||
conference: fixAndroidViewClipping({
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: ColorPalette.appBackground,
|
||||
flex: 1
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* ParticipantView style
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { StatusBar } from 'react-native';
|
||||
import { Immersive } from 'react-native-immersive';
|
||||
|
||||
import { APP_STATE_CHANGED } from '../background';
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_LEFT,
|
||||
@@ -23,9 +24,20 @@ import { MiddlewareRegistry } from '../base/redux';
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
let fullScreen;
|
||||
let fullScreen = null;
|
||||
|
||||
switch (action.type) {
|
||||
case APP_STATE_CHANGED: {
|
||||
// Check if we just came back from the background and reenable full
|
||||
// screen mode if necessary.
|
||||
if (action.appState === 'active') {
|
||||
const conference = store.getState()['features/base/conference'];
|
||||
|
||||
fullScreen = conference ? !conference.audioOnly : false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_WILL_JOIN: {
|
||||
const conference = store.getState()['features/base/conference'];
|
||||
|
||||
@@ -37,10 +49,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
case CONFERENCE_LEFT:
|
||||
fullScreen = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
fullScreen = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fullScreen !== null) {
|
||||
|
||||
@@ -6,8 +6,6 @@ import {
|
||||
} from '../base/tracks';
|
||||
|
||||
import { SELECT_LARGE_VIDEO_PARTICIPANT } from './actionTypes';
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Signals conference to select a participant.
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from './actions';
|
||||
export * from './components';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { setPassword } from '../base/conference';
|
||||
|
||||
import { BEGIN_ROOM_LOCK_REQUEST, END_ROOM_LOCK_REQUEST } from './actionTypes';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Begins a (user) request to lock a specific conference/room.
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './actions';
|
||||
export * from './components';
|
||||
|
||||
import './reducer';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { DISMISS_MOBILE_APP_PROMO } from './actionTypes';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Returns a Redux action which signals that the UnsupportedMobileBrowser which
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './actions';
|
||||
export * from './components';
|
||||
|
||||
import './reducer';
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { BoxModel, ColorPalette, createStyleSheet } from '../../base/styles';
|
||||
import {
|
||||
BoxModel,
|
||||
ColorPalette,
|
||||
createStyleSheet,
|
||||
fixAndroidViewClipping
|
||||
} from '../../base/styles';
|
||||
|
||||
/**
|
||||
* The default color of text on the WelcomePage.
|
||||
@@ -35,11 +40,11 @@ export const styles = createStyleSheet({
|
||||
/**
|
||||
* The style of the top-level container of WelcomePage.
|
||||
*/
|
||||
container: {
|
||||
container: fixAndroidViewClipping({
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: ColorPalette.blue,
|
||||
flex: 1
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* The style of the legal-related content such as (hyper)links to Privacy
|
||||
|
||||
@@ -153,5 +153,10 @@ export default {
|
||||
/**
|
||||
* Indicates that a password is required for the call.
|
||||
*/
|
||||
PASSWORD_REQUIRED: "UI.password_required"
|
||||
PASSWORD_REQUIRED: "UI.password_required",
|
||||
|
||||
/**
|
||||
* Show custom popup/tooltip for a specified button.
|
||||
*/
|
||||
SHOW_CUSTOM_TOOLBAR_BUTTON_POPUP: "UI.show_custom_toolbar_button_popup"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user