Compare commits

..

2 Commits

Author SHA1 Message Date
damencho
4f0f8ea019 fix(tests): Skip iframeAPI if it is disabled. 2025-02-26 14:35:11 -06:00
damencho
444f4a7abc feat(tests): Adds an option for an access jwt token.
Used only for the first participant joining/creating the room.
2025-02-25 09:17:58 -06:00
863 changed files with 13198 additions and 24969 deletions

View File

@@ -17,8 +17,3 @@ react/features/face-landmarks/resources/*
# Not worth it.
actionTypes.ts
# It's not complete until all files are copied at build time.
react-native-sdk/
*.d.ts

View File

@@ -1,5 +1,6 @@
module.exports = {
extends: [
'extends': [
'@jitsi/eslint-config'
]
],
'ignorePatterns': [ '*.d.ts' ]
};

View File

@@ -18,7 +18,7 @@ jobs:
npm -v
- name: Get changed files
id: changed-files
uses: jitsi/changed-files@main
uses: tj-actions/changed-files@v41
- name: Get changed lang files
id: lang-files
run: echo "all=$(echo "${{ steps.changed-files.outputs.all_changed_files }}" | grep -oE 'lang\/\S+' | tr '\n' ' ')" >> "$GITHUB_OUTPUT"
@@ -103,7 +103,7 @@ jobs:
android-sdk-build:
name: Build mobile SDK (Android)
runs-on: ubuntu-latest
container: reactnativecommunity/react-native-android:v18.0
container: reactnativecommunity/react-native-android:v13.0
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4

View File

@@ -1,20 +1,14 @@
# Follow Our Updated Guide to See How You Can Contribute
**Hello there! 👋**
Hello there! 👋
We're thrilled that you're eager to contribute to **Jitsi Meet! ❤️**
We're thrilled that you're eager to contribute to Jitsi Meet! ❤️
Your interest in improving our platform means a lot to us. To ensure your contributions align seamlessly with our goals and processes, we've recently updated our guide. This guide will provide you with clear instructions on how to get involved effectively.
### 📖 Get Started
Ready to get started? Head over to our [Jitsi Meet Handbook](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-contributing/) and let's make **Jitsi Meet** even better together!
### 💬 Join the Discussion
Have questions or need help? Join our community discussions on the [Jitsi Forum](https://community.jitsi.org/) where contributors and maintainers can assist you.
Ready to get started? Head over to our [Jitsi Meet Handbook](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-contributing/) and let's make Jitsi Meet even better together!
### ❗Additional Note
Before sending us your code, double-check that it meets our coding standards. You can do this by running a command: `npm run lint`. If there are any issues, don't worry! You can fix them by running: `npm run lint-fix`. Once your code passes these checks, feel free to submit your pull request.
**Happy coding!**
Happy coding!

View File

@@ -1 +0,0 @@
OK

View File

@@ -1,5 +1,4 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
// Crashlytics integration is done as part of Firebase now, so it gets
// automagically activated with google-services.json
@@ -43,7 +42,6 @@ android {
debug {
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
applicationIdSuffix ".debug"
}
release {
// Uncomment the following line for singing a test release build.
@@ -67,18 +65,9 @@ android {
}
compileOptions {
sourceCompatibility rootProject.ext.javaVersion
targetCompatibility rootProject.ext.javaVersion
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = rootProject.ext.jvmTargetVersion
}
kotlin {
jvmToolchain(rootProject.ext.jvmToolchainVersion)
}
namespace 'org.jitsi.meet'
}
@@ -93,8 +82,10 @@ dependencies {
// Firebase
// - Crashlytics
// - Dynamic Links
implementation 'com.google.firebase:firebase-analytics:21.3.0'
implementation 'com.google.firebase:firebase-crashlytics:18.4.3'
implementation 'com.google.firebase:firebase-dynamic-links:21.1.0'
}
implementation project(':sdk')

View File

@@ -4,8 +4,3 @@
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception
# R8 missing classes - suppress warnings
-dontwarn com.facebook.fresco.ui.common.LoggingListener
-dontwarn com.facebook.memory.config.MemorySpikeConfig
-dontwarn kotlinx.parcelize.Parcelize

View File

@@ -4,6 +4,7 @@ import android.net.Uri;
import android.util.Log;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import org.jitsi.meet.sdk.JitsiMeet;
import org.jitsi.meet.sdk.JitsiMeetActivity;
@@ -21,6 +22,18 @@ final class GoogleServicesHelper {
Log.d(activity.getClass().getSimpleName(), "Initializing Google Services");
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!JitsiMeet.isCrashReportingDisabled(activity));
FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent())
.addOnSuccessListener(activity, pendingDynamicLinkData -> {
Uri dynamicLink = null;
if (pendingDynamicLinkData != null) {
dynamicLink = pendingDynamicLinkData.getLink();
}
if (dynamicLink != null) {
activity.join(dynamicLink.toString());
}
});
}
}
}

View File

@@ -30,12 +30,9 @@ import android.view.KeyEvent;
import androidx.annotation.Nullable;
import com.oney.WebRTCModule.WebRTCModuleOptions;
import org.jitsi.meet.sdk.JitsiMeet;
import org.jitsi.meet.sdk.JitsiMeetActivity;
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
import org.webrtc.Logging;
import java.lang.reflect.Method;
import java.net.URL;
@@ -82,10 +79,6 @@ public class MainActivity extends JitsiMeetActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
JitsiMeet.showSplashScreen(this);
WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();
options.loggingSeverity = Logging.Severity.LS_ERROR;
super.onCreate(null);
}

View File

@@ -5,52 +5,46 @@ import org.gradle.util.VersionNumber
// sub-projects/modules.
buildscript {
ext {
kotlinVersion = "2.0.21"
gradlePluginVersion = "8.4.2"
buildToolsVersion = "34.0.0"
compileSdkVersion = 34
minSdkVersion = 26
targetSdkVersion = 34
supportLibVersion = "28.0.0"
ndkVersion = "27.1.12297006"
// The Maven artifact groupId of the third-party react-native modules which
// Jitsi Meet SDK for Android depends on and which are not available in
// third-party Maven repositories so we have to deploy to a Maven repository
// of ours.
moduleGroupId = 'com.facebook.react'
// Maven repo where artifacts will be published
mavenRepo = System.env.MVN_REPO ?: ""
mavenUser = System.env.MVN_USER ?: ""
mavenPassword = System.env.MVN_PASSWORD ?: ""
// Libre build
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
//React Native and Hermes Version
rnVersion = "0.77.2"
// Java dependencies
javaVersion = JavaVersion.VERSION_17
jvmToolchainVersion = 17
jvmTargetVersion = '17'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion"
classpath "com.android.tools.build:gradle:$rootProject.ext.gradlePluginVersion"
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.google.gms:google-services:4.4.0'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
}
}
ext {
kotlinVersion = "1.9.24"
buildToolsVersion = "34.0.0"
compileSdkVersion = 34
minSdkVersion = 26
targetSdkVersion = 34
supportLibVersion = "28.0.0"
ndkVersion = "26.1.10909125"
// The Maven artifact groupId of the third-party react-native modules which
// Jitsi Meet SDK for Android depends on and which are not available in
// third-party Maven repositories so we have to deploy to a Maven repository
// of ours.
moduleGroupId = 'com.facebook.react'
// Maven repo where artifacts will be published
mavenRepo = System.env.MVN_REPO ?: ""
mavenUser = System.env.MVN_USER ?: ""
mavenPassword = System.env.MVN_PASSWORD ?: ""
// Libre build
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
//React Native and Hermes Version
rnVersion = "0.75.5"
}
allprojects {
repositories {
mavenCentral()
@@ -140,7 +134,7 @@ allprojects {
project.version = "${json.version}-jitsi-${versionQualifierNumber}"
task jitsiAndroidSourcesJar(type: Jar) {
archiveClassifier = 'sources'
classifier = 'sources'
from android.sourceSets.main.java.source
}
@@ -191,46 +185,16 @@ allprojects {
}
}
// Force the version of the Android build tools we have chosen on all subprojects.
// Force the version of the Android build tools we have chosen on all
// subprojects. The forcing was introduced for react-native and the third-party
// modules that we utilize such as react-native-background-timer.
subprojects { subproject ->
afterEvaluate{
if ((subproject.plugins.hasPlugin('android')
|| subproject.plugins.hasPlugin('android-library'))
&& rootProject.ext.has('buildToolsVersion')) {
android {
buildToolsVersion rootProject.ext.buildToolsVersion
buildFeatures {
buildConfig true
}
// Set JVM target across all subprojects
compileOptions {
sourceCompatibility rootProject.ext.javaVersion
targetCompatibility rootProject.ext.javaVersion
}
// Disable lint errors for problematic third-party modules
// react-native-background-timer
// react-native-calendar-events
lint {
abortOnError = false
}
}
}
// Add Kotlin configuration for subprojects that use Kotlin
if (subproject.plugins.hasPlugin('kotlin-android')) {
subproject.kotlin {
jvmToolchain(rootProject.ext.jvmToolchainVersion)
}
// Set Kotlin JVM target
subproject.android {
kotlinOptions {
jvmTarget = rootProject.ext.jvmTargetVersion
}
}
}
}

View File

@@ -11,26 +11,20 @@
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx4048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# This one fixes a weird WebRTC runtime problem on some devices.
# https://github.com/jitsi/jitsi-meet/issues/7911#issuecomment-714323255
android.enableDexingArtifactTransform.desugaring=false
android.useAndroidX=true
android.enableJetifier=true
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
hermesEnabled=true
android.bundle.enableUncompressedNativeLibs=false
appVersion=99.0.0
sdkVersion=0.0.0

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -44,13 +44,15 @@ dependencies {
api "com.facebook.react:react-android:$rootProject.ext.rnVersion"
api "com.facebook.react:hermes-android:$rootProject.ext.rnVersion"
//noinspection GradleDynamicVersion
implementation 'org.webkit:android-jsc:+'
implementation 'com.facebook.fresco:animated-gif:2.5.0'
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
implementation 'com.jakewharton.timber:timber:5.0.1'
implementation 'com.squareup.duktape:duktape-android:1.3.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'androidx.startup:startup-runtime:1.1.0'
implementation 'com.google.j2objc:j2objc-annotations:3.0.0'
// Only add these packages if we are NOT doing a LIBRE_BUILD
if (!rootProject.ext.libreBuild) {
@@ -84,14 +86,12 @@ dependencies {
implementation project(':react-native-screens')
implementation project(':react-native-slider')
implementation project(':react-native-sound')
implementation project(':react-native-splash-view')
implementation project(':react-native-splash-screen')
implementation project(':react-native-svg')
implementation project(':react-native-video')
implementation project(':react-native-webrtc')
implementation project(':react-native-webview')
// Use `api` here so consumers can use WebRTCModuleOptions.
api project(':react-native-webrtc')
testImplementation 'junit:junit:4.12'
}
@@ -138,16 +138,8 @@ android.libraryVariants.all { def variant ->
def devEnabled = !targetName.toLowerCase().contains("release")
// Run the bundler
// Use full path to node to avoid PATH issues in Gradle
def nodePath = System.getenv('NVM_BIN') ? "${System.getenv('NVM_BIN')}/node" : "node"
// Debug: Print the node path and environment
println "Using node path: ${nodePath}"
println "NVM_BIN: ${System.getenv('NVM_BIN')}"
println "Working directory: ${reactRoot}"
commandLine(
nodePath,
"node",
"node_modules/react-native/scripts/bundle.js",
"--platform", "android",
"--dev", "${devEnabled}",
@@ -160,70 +152,6 @@ android.libraryVariants.all { def variant ->
enabled !devEnabled
}
// GRADLE REQUIREMENTS (Gradle 8.7+ / AGP 8.5.0+):
// This task requires explicit dependencies on resource tasks from all React Native modules
// due to Gradle's strict validation of task dependencies.
// Without these dependencies,
// builds will fail with errors like:
// "Task ':sdk:bundleReleaseJsAndAssets' uses the output of task ':react-native-amplitude:packageReleaseResources'
// without declaring a dependency on it."
// The automatic dependency resolution below ensures all required resource tasks are properly
// declared as dependencies before this task executes.
if (variant.name.toLowerCase().contains("release")) {
rootProject.subprojects.each { subproject ->
if (
subproject.name.startsWith("react-native-") ||
subproject.name.startsWith("@react-native-") ||
subproject.name.startsWith("@giphy/")
) {
[
"packageReleaseResources",
"generateReleaseResValues",
"generateReleaseResources",
"generateReleaseBuildConfig",
"processReleaseManifest",
"writeReleaseAarMetadata",
"generateReleaseRFile",
"compileReleaseLibraryResources",
"compileReleaseJavaWithJavac",
"javaPreCompileRelease",
"bundleLibCompileToJarRelease",
"exportReleaseConsumerProguardFiles",
"mergeReleaseGeneratedProguardFiles",
"mergeReleaseJniLibFolders",
"mergeReleaseShaders",
"packageReleaseAssets",
"processReleaseJavaRes",
"prepareReleaseArtProfile",
"copyReleaseJniLibsProjectOnly",
"extractDeepLinksRelease",
"createFullJarRelease",
"generateReleaseLintModel",
"writeReleaseLintModelMetadata",
"generateReleaseLintVitalModel",
"lintVitalAnalyzeRelease",
"lintReportRelease",
"lintAnalyzeRelease",
"lintReportDebug",
"lintAnalyzeDebug"
].each { taskName ->
if (subproject.tasks.findByName(taskName)) {
currentBundleTask.dependsOn(subproject.tasks.named(taskName))
}
}
// Also depend on the main build task to ensure all sub-tasks are completed
if (subproject.tasks.findByName("build")) {
currentBundleTask.dependsOn(subproject.tasks.named("build"))
}
}
}
}
currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)
variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)

View File

@@ -3,6 +3,12 @@ package org.jitsi.meet.sdk;
import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.bridge.WritableNativeMap;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
/**
* Wraps the name and extra data for events that were broadcasted locally.
*/
@@ -10,21 +16,57 @@ public class BroadcastAction {
private static final String TAG = BroadcastAction.class.getSimpleName();
private final Type type;
private final Bundle data;
private final HashMap<String, Object> data;
public BroadcastAction(Intent intent) {
this.type = Type.buildTypeFromAction(intent.getAction());
this.data = intent.getExtras();
this.data = buildDataFromBundle(intent.getExtras());
}
public Type getType() {
return this.type;
}
public Bundle getData() {
public HashMap<String, Object> getData() {
return this.data;
}
public WritableNativeMap getDataAsWritableNativeMap() {
WritableNativeMap nativeMap = new WritableNativeMap();
for (String key : this.data.keySet()) {
try {
if (this.data.get(key) instanceof Boolean) {
nativeMap.putBoolean(key, (Boolean) this.data.get(key));
} else if (this.data.get(key) instanceof Integer) {
nativeMap.putInt(key, (Integer) this.data.get(key));
} else if (this.data.get(key) instanceof Double) {
nativeMap.putDouble(key, (Double) this.data.get(key));
} else if (this.data.get(key) instanceof String) {
nativeMap.putString(key, (String) this.data.get(key));
} else {
throw new Exception("Unsupported extra data type");
}
} catch (Exception e) {
JitsiMeetLogger.w(TAG + " invalid extra data in event", e);
}
}
return nativeMap;
}
private static HashMap<String, Object> buildDataFromBundle(Bundle bundle) {
HashMap<String, Object> map = new HashMap<>();
if (bundle != null) {
for (String key : bundle.keySet()) {
map.put(key, bundle.get(key));
}
}
return map;
}
enum Type {
SET_AUDIO_MUTED("org.jitsi.meet.SET_AUDIO_MUTED"),
HANG_UP("org.jitsi.meet.HANG_UP"),
@@ -40,9 +82,7 @@ public class BroadcastAction {
SHOW_NOTIFICATION("org.jitsi.meet.SHOW_NOTIFICATION"),
HIDE_NOTIFICATION("org.jitsi.meet.HIDE_NOTIFICATION"),
START_RECORDING("org.jitsi.meet.START_RECORDING"),
STOP_RECORDING("org.jitsi.meet.STOP_RECORDING"),
OVERWRITE_CONFIG("org.jitsi.meet.OVERWRITE_CONFIG"),
SEND_CAMERA_FACING_MODE_MESSAGE("org.jitsi.meet.SEND_CAMERA_FACING_MODE_MESSAGE");
STOP_RECORDING("org.jitsi.meet.STOP_RECORDING");
private final String action;

View File

@@ -91,9 +91,7 @@ public class BroadcastEvent {
VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED"),
READY_TO_CLOSE("org.jitsi.meet.READY_TO_CLOSE"),
TRANSCRIPTION_CHUNK_RECEIVED("org.jitsi.meet.TRANSCRIPTION_CHUNK_RECEIVED"),
CUSTOM_BUTTON_PRESSED("org.jitsi.meet.CUSTOM_BUTTON_PRESSED"),
CONFERENCE_UNIQUE_ID_SET("org.jitsi.meet.CONFERENCE_UNIQUE_ID_SET"),
RECORDING_STATUS_CHANGED("org.jitsi.meet.RECORDING_STATUS_CHANGED");
CUSTOM_BUTTON_PRESSED("org.jitsi.meet.CUSTOM_BUTTON_PRESSED");
private static final String CONFERENCE_BLURRED_NAME = "CONFERENCE_BLURRED";
private static final String CONFERENCE_FOCUSED_NAME = "CONFERENCE_FOCUSED";
@@ -112,8 +110,6 @@ public class BroadcastEvent {
private static final String READY_TO_CLOSE_NAME = "READY_TO_CLOSE";
private static final String TRANSCRIPTION_CHUNK_RECEIVED_NAME = "TRANSCRIPTION_CHUNK_RECEIVED";
private static final String CUSTOM_BUTTON_PRESSED_NAME = "CUSTOM_BUTTON_PRESSED";
private static final String CONFERENCE_UNIQUE_ID_SET_NAME = "CONFERENCE_UNIQUE_ID_SET";
private static final String RECORDING_STATUS_CHANGED_NAME = "RECORDING_STATUS_CHANGED";
private final String action;
@@ -170,10 +166,6 @@ public class BroadcastEvent {
return TRANSCRIPTION_CHUNK_RECEIVED;
case CUSTOM_BUTTON_PRESSED_NAME:
return CUSTOM_BUTTON_PRESSED;
case CONFERENCE_UNIQUE_ID_SET_NAME:
return CONFERENCE_UNIQUE_ID_SET;
case RECORDING_STATUS_CHANGED_NAME:
return RECORDING_STATUS_CHANGED;
}
return null;

View File

@@ -139,19 +139,4 @@ public class BroadcastIntentHelper {
return intent;
}
public static Intent buildOverwriteConfigIntent(Bundle config) {
Intent intent = new Intent(BroadcastAction.Type.OVERWRITE_CONFIG.getAction());
intent.putExtra("config", config);
return intent;
}
public static Intent buildSendCameraFacingModeMessageIntent(String to, String facingMode) {
Intent intent = new Intent(BroadcastAction.Type.SEND_CAMERA_FACING_MODE_MESSAGE.getAction());
intent.putExtra("to", to);
intent.putExtra("facingMode", facingMode);
return intent;
}
}

View File

@@ -3,9 +3,6 @@ package org.jitsi.meet.sdk;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import com.facebook.react.bridge.Arguments;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@@ -31,14 +28,7 @@ public class BroadcastReceiver extends android.content.BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
BroadcastAction action = new BroadcastAction(intent);
String actionName = action.getType().getAction();
Bundle data = action.getData();
// For actions without data bundle (like hangup), we create an empty map
// instead of attempting to convert a null bundle to avoid crashes.
if (data != null) {
ReactInstanceManagerHolder.emitEvent(actionName, Arguments.fromBundle(data));
} else {
ReactInstanceManagerHolder.emitEvent(actionName, Arguments.createMap());
}
ReactInstanceManagerHolder.emitEvent(actionName, action.getDataAsWritableNativeMap());
}
}

View File

@@ -101,8 +101,6 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
constants.put("HIDE_NOTIFICATION", BroadcastAction.Type.HIDE_NOTIFICATION.getAction());
constants.put("START_RECORDING", BroadcastAction.Type.START_RECORDING.getAction());
constants.put("STOP_RECORDING", BroadcastAction.Type.STOP_RECORDING.getAction());
constants.put("OVERWRITE_CONFIG", BroadcastAction.Type.OVERWRITE_CONFIG.getAction());
constants.put("SEND_CAMERA_FACING_MODE_MESSAGE", BroadcastAction.Type.SEND_CAMERA_FACING_MODE_MESSAGE.getAction());
return constants;
}

View File

@@ -23,10 +23,8 @@ import androidx.annotation.NonNull;
import androidx.startup.Initializer;
import com.facebook.soloader.SoLoader;
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
import org.wonday.orientation.OrientationActivityLifecycle;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -37,11 +35,7 @@ public class JitsiInitializer implements Initializer<Boolean> {
public Boolean create(@NonNull Context context) {
Log.d(this.getClass().getCanonicalName(), "create");
try {
SoLoader.init(context, OpenSourceMergedSoMapping.INSTANCE);
} catch (IOException e) {
throw new RuntimeException(e);
}
SoLoader.init(context, /* native exopackage */ false);
// Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register();
@@ -49,10 +43,6 @@ public class JitsiInitializer implements Initializer<Boolean> {
// Register activity lifecycle handler for the orientation locker module.
((Application) context).registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
// Initialize ReactInstanceManager during application startup
// This ensures it's ready before any Activity onCreate is called
ReactInstanceManagerHolder.initReactInstanceManager((Application) context);
return true;
}

View File

@@ -22,7 +22,7 @@ import android.os.Bundle;
import com.facebook.react.ReactInstanceManager;
import com.splashview.SplashView;
import org.devio.rn.splashscreen.SplashScreen;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
public class JitsiMeet {
@@ -92,7 +92,7 @@ public class JitsiMeet {
*/
public static void showSplashScreen(Activity activity) {
try {
SplashView.INSTANCE.showSplashView(activity);
SplashScreen.show(activity);
} catch (Exception e) {
JitsiMeetLogger.e(e, "Failed to show splash screen");
}

View File

@@ -102,10 +102,6 @@ public class JitsiMeetActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ReactInstanceManager is now initialized by JitsiInitializer during application startup
// Just call onHostResume since the manager is already ready
JitsiMeetActivityDelegate.onHostResume(this);
setContentView(R.layout.activity_jitsi_meet);
this.jitsiView = findViewById(R.id.jitsiView);
@@ -276,16 +272,8 @@ public class JitsiMeetActivity extends AppCompatActivity
// }
// protected void onCustomButtonPressed(HashMap<String, Object> extraData) {
// JitsiMeetLogger.i("Custom button pressed: " + extraData);
// }
// protected void onConferenceUniqueIdSet(HashMap<String, Object> extraData) {
// JitsiMeetLogger.i("Conference unique id set: " + extraData);
// }
// protected void onRecordingStatusChanged(HashMap<String, Object> extraData) {
// JitsiMeetLogger.i("Recording status changed: " + extraData);
// }
// JitsiMeetLogger.i("Custom button pressed: " + extraData);
// }
// Activity lifecycle methods
//
@@ -370,18 +358,12 @@ public class JitsiMeetActivity extends AppCompatActivity
case READY_TO_CLOSE:
onReadyToClose();
break;
// case TRANSCRIPTION_CHUNK_RECEIVED:
// onTranscriptionChunkReceived(event.getData());
// break;
// case CUSTOM_BUTTON_PRESSED:
// onCustomButtonPressed(event.getData());
// break;
// case CONFERENCE_UNIQUE_ID_SET:
// onConferenceUniqueIdSet(event.getData());
// break;
// case RECORDING_STATUS_CHANGED:
// onRecordingStatusChanged(event.getData());
// break;
// case TRANSCRIPTION_CHUNK_RECEIVED:
// onTranscriptionChunkReceived(event.getData());
// break;
// case CUSTOM_BUTTON_PRESSED:
// onCustomButtonPressed(event.getData());
// break;
}
}
}

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -34,9 +33,10 @@ import org.jitsi.meet.sdk.log.JitsiMeetLogger;
public class JitsiMeetView extends FrameLayout {
/**
* Background color. Should match the background color set in JS.
* Background color used by {@code BaseReactView} and the React Native root
* view.
*/
private static final int BACKGROUND_COLOR = 0xFF040404;
private static final int BACKGROUND_COLOR = 0xFF111111;
/**
* React Native root view.
@@ -197,6 +197,8 @@ public class JitsiMeetView extends FrameLayout {
}
setBackgroundColor(BACKGROUND_COLOR);
ReactInstanceManagerHolder.initReactInstanceManager((Activity) context);
}
/**

View File

@@ -1,84 +0,0 @@
package org.jitsi.meet.sdk;
/*
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
import android.media.MediaCodecInfo;
import androidx.annotation.Nullable;
import com.oney.WebRTCModule.webrtcutils.SoftwareVideoDecoderFactoryProxy;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoDecoderFactory;
import org.webrtc.JitsiPlatformVideoDecoderFactory;
import org.webrtc.Predicate;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoDecoder;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoDecoderFallback;
import java.util.Arrays;
import java.util.LinkedHashSet;
/**
* Custom decoder factory which uses HW decoders and falls back to SW.
*/
public class JitsiVideoDecoderFactory implements VideoDecoderFactory {
private final VideoDecoderFactory hardwareVideoDecoderFactory;
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactoryProxy();
private final VideoDecoderFactory platformSoftwareVideoDecoderFactory;
/**
* Predicate to filter out the AV1 hardware decoder, as we've seen decoding issues with it.
*/
private static final String GOOGLE_AV1_DECODER = "c2.google.av1";
private static final Predicate<MediaCodecInfo> hwCodecPredicate = arg -> {
// Filter out the Google AV1 codec.
return !arg.getName().startsWith(GOOGLE_AV1_DECODER);
};
private static final Predicate<MediaCodecInfo> swCodecPredicate = arg -> {
// Noop, just making sure we can customize it easily if needed.
return true;
};
/**
* Create decoder factory using default hardware decoder factory.
*/
public JitsiVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext, hwCodecPredicate);
this.platformSoftwareVideoDecoderFactory = new JitsiPlatformVideoDecoderFactory(eglContext, swCodecPredicate);
}
@Override
public @Nullable VideoDecoder createDecoder(VideoCodecInfo codecType) {
VideoDecoder softwareDecoder = softwareVideoDecoderFactory.createDecoder(codecType);
final VideoDecoder hardwareDecoder = hardwareVideoDecoderFactory.createDecoder(codecType);
if (softwareDecoder == null) {
softwareDecoder = platformSoftwareVideoDecoderFactory.createDecoder(codecType);
}
if (hardwareDecoder != null && softwareDecoder != null) {
// Both hardware and software supported, wrap it in a software fallback
return new VideoDecoderFallback(
/* fallback= */ softwareDecoder, /* primary= */ hardwareDecoder);
}
return hardwareDecoder != null ? hardwareDecoder : softwareDecoder;
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
LinkedHashSet<VideoCodecInfo> supportedCodecInfos = new LinkedHashSet<>();
supportedCodecInfos.addAll(Arrays.asList(softwareVideoDecoderFactory.getSupportedCodecs()));
supportedCodecInfos.addAll(Arrays.asList(hardwareVideoDecoderFactory.getSupportedCodecs()));
supportedCodecInfos.addAll(Arrays.asList(platformSoftwareVideoDecoderFactory.getSupportedCodecs()));
return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
}
}

View File

@@ -1,16 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
import org.webrtc.EglBase;
/**
* Custom encoder factory which uses HW for H.264 and SW for everything else.
*/
public class JitsiVideoEncoderFactory extends H264AndSoftwareVideoEncoderFactory {
public JitsiVideoEncoderFactory(@Nullable EglBase.Context eglContext) {
super(eglContext);
}
}

View File

@@ -16,9 +16,8 @@
package org.jitsi.meet.sdk;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -33,9 +32,12 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.oney.WebRTCModule.EglUtils;
import com.oney.WebRTCModule.WebRTCModuleOptions;
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoDecoderFactory;
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.devio.rn.splashscreen.SplashScreenModule;
import org.webrtc.EglBase;
import org.webrtc.Logging;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
@@ -68,6 +70,7 @@ class ReactInstanceManagerHolder {
new JavaScriptSandboxModule(reactContext),
new LocaleDetector(reactContext),
new LogBridgeModule(reactContext),
new SplashScreenModule(reactContext),
new PictureInPictureModule(reactContext),
new ProximityModule(reactContext),
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
@@ -89,7 +92,7 @@ class ReactInstanceManagerHolder {
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.calendarevents.RNCalendarEventsPackage(),
new com.sayem.keepawake.KCKeepAwakePackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.reactnativecommunity.clipboard.ClipboardPackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
@@ -109,7 +112,6 @@ class ReactInstanceManagerHolder {
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
new com.horcrux.svg.SvgPackage(),
new org.wonday.orientation.OrientationPackage(),
new com.splashview.SplashViewPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
@@ -124,31 +126,31 @@ class ReactInstanceManagerHolder {
// AmplitudeReactNativePackage
try {
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
Constructor<?> constructor = amplitudePackageClass.getConstructor();
Constructor constructor = amplitudePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
JitsiMeetLogger.d(TAG, "Not loading AmplitudeReactNativePackage");
Log.d(TAG, "Not loading AmplitudeReactNativePackage");
}
// GiphyReactNativeSdkPackage
try {
Class<?> giphyPackageClass = Class.forName("com.giphyreactnativesdk.RTNGiphySdkPackage");
Constructor<?> constructor = giphyPackageClass.getConstructor();
Class<?> giphyPackageClass = Class.forName("com.giphyreactnativesdk.GiphyReactNativeSdkPackage");
Constructor constructor = giphyPackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
JitsiMeetLogger.d(TAG, "Not loading GiphyReactNativeSdkPackage");
Log.d(TAG, "Not loading GiphyReactNativeSdkPackage");
}
// RNGoogleSignInPackage
try {
Class<?> googlePackageClass = Class.forName("com.reactnativegooglesignin.RNGoogleSigninPackage");
Constructor<?> constructor = googlePackageClass.getConstructor();
Constructor constructor = googlePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
JitsiMeetLogger.d(TAG, "Not loading RNGoogleSignInPackage");
Log.d(TAG, "Not loading RNGoogleSignInPackage");
}
return packages;
@@ -167,7 +169,7 @@ class ReactInstanceManagerHolder {
= ReactInstanceManagerHolder.getReactInstanceManager();
if (reactInstanceManager != null) {
@SuppressLint("VisibleForTests") ReactContext reactContext
ReactContext reactContext
= reactInstanceManager.getCurrentReactContext();
if (reactContext != null) {
@@ -190,7 +192,7 @@ class ReactInstanceManagerHolder {
*/
static <T extends NativeModule> T getNativeModule(
Class<T> nativeModuleClass) {
@SuppressLint("VisibleForTests") ReactContext reactContext
ReactContext reactContext
= reactInstanceManager != null
? reactInstanceManager.getCurrentReactContext() : null;
@@ -208,38 +210,35 @@ class ReactInstanceManagerHolder {
* time. All {@code ReactRootView} instances will be tied to the one and
* only {@code ReactInstanceManager}.
*
* @param app {@code Application}
* @param activity {@code Activity} current running Activity.
*/
static void initReactInstanceManager(Application app) {
static void initReactInstanceManager(Activity activity) {
if (reactInstanceManager != null) {
return;
}
// Initialize the WebRTC module options.
WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();
options.enableMediaProjectionService = true;
if (options.videoDecoderFactory == null || options.videoEncoderFactory == null) {
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
if (options.videoDecoderFactory == null) {
options.videoDecoderFactory = new JitsiVideoDecoderFactory(eglContext);
}
if (options.videoEncoderFactory == null) {
options.videoEncoderFactory = new JitsiVideoEncoderFactory(eglContext);
}
}
JitsiMeetLogger.d(TAG, "initializing RN");
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
options.videoDecoderFactory = new H264AndSoftwareVideoDecoderFactory(eglContext);
options.videoEncoderFactory = new H264AndSoftwareVideoEncoderFactory(eglContext);
options.enableMediaProjectionService = true;
// options.loggingSeverity = Logging.Severity.LS_INFO;
Log.d(TAG, "initializing RN with Activity");
reactInstanceManager
= ReactInstanceManager.builder()
.setApplication(app)
.setCurrentActivity(null)
.setApplication(activity.getApplication())
.setCurrentActivity(activity)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.setJavaScriptExecutorFactory(new HermesExecutorFactory())
.addPackages(getReactNativePackages())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import android.media.MediaCodecInfo;
import androidx.annotation.Nullable;
/** Factory for Android platform software VideoDecoders. */
public class JitsiPlatformVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
/**
* Default allowed predicate.
*/
private static final Predicate<MediaCodecInfo> defaultAllowedPredicate =
codecInfo -> {
// We only want to use the platform software codecs.
return MediaCodecUtils.isSoftwareOnly(codecInfo);
};
/**
* Creates a PlatformSoftwareVideoDecoderFactory that supports surface texture rendering.
*
* @param sharedContext The textures generated will be accessible from this context. May be null,
* this disables texture support.
*/
public JitsiPlatformVideoDecoderFactory(@Nullable EglBase.Context sharedContext) {
super(sharedContext, defaultAllowedPredicate);
}
public JitsiPlatformVideoDecoderFactory(@Nullable EglBase.Context sharedContext, @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
super(sharedContext, codecAllowedPredicate == null ? defaultAllowedPredicate : codecAllowedPredicate.and(defaultAllowedPredicate));
}
}

View File

@@ -1,7 +1,9 @@
rootProject.name = 'jitsi-meet'
include ':app', ':sdk'
include ':react-native-amplitude'
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/analytics-react-native/android')
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native/android')
include ':react-native-async-storage'
project(':react-native-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-async-storage/async-storage/android')
include ':react-native-background-timer'
@@ -27,7 +29,7 @@ project(':react-native-google-signin').projectDir = new File(rootProject.project
include ':react-native-immersive-mode'
project(':react-native-immersive-mode').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive-mode/android')
include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/@sayem314/react-native-keep-awake/android')
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
include ':react-native-orientation-locker'
project(':react-native-orientation-locker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation-locker/android')
include ':react-native-pager-view'
@@ -42,8 +44,8 @@ include ':react-native-slider'
project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
include ':react-native-sound'
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
include ':react-native-splash-view'
project(':react-native-splash-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-view/android')
include ':react-native-splash-screen'
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-video'

26
app.js
View File

@@ -10,32 +10,6 @@ import '@matrix-org/olm';
import 'focus-visible';
/*
* Safari polyfill for createImageBitmap
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap
*
* Support source image types: Canvas.
*/
if (!('createImageBitmap' in window)) {
window.createImageBitmap = function(data) {
return new Promise((resolve, reject) => {
let dataURL;
if (data instanceof HTMLCanvasElement) {
dataURL = data.toDataURL();
} else {
reject(new Error('createImageBitmap does not handle the provided image source type'));
}
const img = document.createElement('img');
img.addEventListener('load', () => {
resolve(img);
});
img.src = dataURL;
});
};
}
// We need to setup the jitsi-local-storage as early as possible so that we can start using it.
// NOTE: If jitsi-local-storage is used before the initial setup is performed this will break the use case when we use
// the local storage from the parent page when the localStorage is disabled. Also the setup is relying that

View File

@@ -1,5 +1,5 @@
module.exports = {
presets: [ 'module:@react-native/babel-preset' ],
presets: [ 'module:metro-react-native-babel-preset' ],
env: {
production: {
plugins: [ 'react-native-paper/babel' ]

View File

@@ -18,6 +18,8 @@ import {
maybeRedirectToWelcomePage,
reloadWithStoredParams
} from './react/features/app/actions';
import { showModeratedNotification } from './react/features/av-moderation/actions';
import { shouldShowModeratedNotification } from './react/features/av-moderation/functions';
import {
_conferenceWillJoin,
authStatusChanged,
@@ -50,8 +52,7 @@ import {
commonUserJoinedHandling,
commonUserLeftHandling,
getConferenceOptions,
sendLocalParticipant,
updateTrackMuteState
sendLocalParticipant
} from './react/features/base/conference/functions';
import { getReplaceParticipant, getSsrcRewritingFeatureFlag } from './react/features/base/config/functions';
import { connect } from './react/features/base/connection/actions.web';
@@ -88,7 +89,7 @@ import {
setVideoMuted,
setVideoUnmutePermissions
} from './react/features/base/media/actions';
import { MEDIA_TYPE, VIDEO_MUTISM_AUTHORITY, VIDEO_TYPE } from './react/features/base/media/constants';
import { MEDIA_TYPE, VIDEO_TYPE } from './react/features/base/media/constants';
import {
getStartWithAudioMuted,
getStartWithVideoMuted,
@@ -130,6 +131,7 @@ import {
createLocalTracksF,
getLocalJitsiAudioTrack,
getLocalJitsiVideoTrack,
getLocalTracks,
getLocalVideoTrack,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute
@@ -152,6 +154,7 @@ import {
DATA_CHANNEL_CLOSED_NOTIFICATION_ID,
NOTIFICATION_TIMEOUT_TYPE
} from './react/features/notifications/constants';
import { isModerationNotificationDisplayed } from './react/features/notifications/functions';
import { suspendDetected } from './react/features/power-monitor/actions';
import { initPrejoin, isPrejoinPageVisible } from './react/features/prejoin/functions';
import { disableReceiver, stopReceiver } from './react/features/remote-control/actions';
@@ -203,6 +206,23 @@ function sendData(command, value) {
room.sendCommand(command, { value });
}
/**
* Mute or unmute local audio stream if it exists.
* @param {boolean} muted - if audio stream should be muted or unmuted.
*/
function muteLocalAudio(muted) {
APP.store.dispatch(setAudioMuted(muted));
}
/**
* Mute or unmute local video stream if it exists.
* @param {boolean} muted if video stream should be muted or unmuted.
*
*/
function muteLocalVideo(muted) {
APP.store.dispatch(setVideoMuted(muted));
}
/**
* A queue for the async replaceLocalTrack action so that multiple audio
* replacements cannot happen simultaneously. This solves the issue where
@@ -581,7 +601,7 @@ export default {
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions, true);
tryCreateLocalTracks.then(tr => {
tryCreateLocalTracks.then(async tr => {
const createLocalTracksEnd = window.performance.now();
connectionTimes['conference.init.createLocalTracks.end'] = createLocalTracksEnd;
@@ -689,10 +709,11 @@ export default {
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
*
* @param {boolean} mute true for mute and false for unmute.
* @param {boolean} [showUI] when set to false will not display any error
* dialogs in case of media permissions error.
* @returns {Promise}
*/
async muteAudio(mute) {
async muteAudio(mute, showUI = true) {
const state = APP.store.getState();
if (!mute
@@ -702,7 +723,56 @@ export default {
return;
}
await APP.store.dispatch(setAudioMuted(mute, true));
// check for A/V Moderation when trying to unmute
if (!mute && shouldShowModeratedNotification(MEDIA_TYPE.AUDIO, state)) {
if (!isModerationNotificationDisplayed(MEDIA_TYPE.AUDIO, state)) {
APP.store.dispatch(showModeratedNotification(MEDIA_TYPE.AUDIO));
}
return;
}
// Not ready to modify track's state yet
if (!this._localTracksInitialized) {
// This will only modify base/media.audio.muted which is then synced
// up with the track at the end of local tracks initialization.
muteLocalAudio(mute);
this.updateAudioIconEnabled();
return;
} else if (this.isLocalAudioMuted() === mute) {
// NO-OP
return;
}
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
if (!localAudio && !mute) {
const maybeShowErrorDialog = error => {
showUI && APP.store.dispatch(notifyMicError(error));
};
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO ], IGUMPendingState.PENDING_UNMUTE));
await createLocalTracksF({ devices: [ 'audio' ] })
.then(([ audioTrack ]) => audioTrack)
.catch(error => {
maybeShowErrorDialog(error);
// Rollback the audio muted status by using null track
return null;
})
.then(async audioTrack => {
await this._maybeApplyAudioMixerEffect(audioTrack);
return this.useAudioStream(audioTrack);
})
.finally(() => {
APP.store.dispatch(gumPending([ MEDIA_TYPE.AUDIO ], IGUMPendingState.NONE));
});
} else {
muteLocalAudio(mute);
}
},
/**
@@ -732,9 +802,16 @@ export default {
/**
* Simulates toolbar button click for video mute. Used by shortcuts and API.
* @param mute true for mute and false for unmute.
* @param {boolean} [showUI] when set to false will not display any error
* dialogs in case of media permissions error.
*/
muteVideo(mute) {
muteVideo(mute, showUI = true) {
if (this.videoSwitchInProgress) {
logger.warn('muteVideo - unable to perform operations while video switch is in progress');
return;
}
const state = APP.store.getState();
if (!mute
@@ -744,7 +821,65 @@ export default {
return;
}
APP.store.dispatch(setVideoMuted(mute, VIDEO_MUTISM_AUTHORITY.USER, true));
// check for A/V Moderation when trying to unmute and return early
if (!mute && shouldShowModeratedNotification(MEDIA_TYPE.VIDEO, state)) {
return;
}
// If not ready to modify track's state yet adjust the base/media
if (!this._localTracksInitialized) {
// This will only modify base/media.video.muted which is then synced
// up with the track at the end of local tracks initialization.
muteLocalVideo(mute);
this.setVideoMuteStatus();
return;
} else if (this.isLocalVideoMuted() === mute) {
// NO-OP
return;
}
const localVideo = getLocalJitsiVideoTrack(state);
if (!localVideo && !mute && !this.isCreatingLocalTrack) {
const maybeShowErrorDialog = error => {
showUI && APP.store.dispatch(notifyCameraError(error));
};
this.isCreatingLocalTrack = true;
APP.store.dispatch(gumPending([ MEDIA_TYPE.VIDEO ], IGUMPendingState.PENDING_UNMUTE));
// Try to create local video if there wasn't any.
// This handles the case when user joined with no video
// (dismissed screen sharing screen or in audio only mode), but
// decided to add it later on by clicking on muted video icon or
// turning off the audio only mode.
//
// FIXME when local track creation is moved to react/redux
// it should take care of the use case described above
createLocalTracksF({ devices: [ 'video' ] })
.then(([ videoTrack ]) => videoTrack)
.catch(error => {
// FIXME should send some feedback to the API on error ?
maybeShowErrorDialog(error);
// Rollback the video muted status by using null track
return null;
})
.then(videoTrack => {
logger.debug(`muteVideo: calling useVideoStream for track: ${videoTrack}`);
return this.useVideoStream(videoTrack);
})
.finally(() => {
this.isCreatingLocalTrack = false;
APP.store.dispatch(gumPending([ MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
});
} else {
// FIXME show error dialog if it fails (should be handled by react)
muteLocalVideo(mute);
}
},
/**
@@ -925,14 +1060,6 @@ export default {
downloadJSON(logs, filename);
},
/**
* Download app state, a function that can be called from console while debugging.
* @param filename (optional) specify target filename
*/
saveState(filename = 'meet-state.json') {
downloadJSON(APP.store.getState(), filename);
},
/**
* Exposes a Command(s) API on this instance. It is necessitated by (1) the
* desire to keep room private to this instance and (2) the need of other
@@ -997,6 +1124,7 @@ export default {
// Restore initial state.
this._localTracksInitialized = false;
this.isSharingScreen = false;
this.roomName = roomName;
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(options);
@@ -1175,6 +1303,8 @@ export default {
return Boolean(APP.store.getState()['features/base/audio-only'].enabled);
},
videoSwitchInProgress: false,
/**
* This fields stores a handler which will create a Promise which turns off
* the screen sharing and restores the previous video state (was there
@@ -1203,6 +1333,7 @@ export default {
*/
async _turnScreenSharingOff(didHaveVideo, ignoreDidHaveVideo) {
this._untoggleScreenSharing = null;
this.videoSwitchInProgress = true;
APP.store.dispatch(stopReceiver());
@@ -1254,11 +1385,13 @@ export default {
return promise.then(
() => {
this.videoSwitchInProgress = false;
sendAnalytics(createScreenSharingEvent('stopped',
duration === 0 ? null : duration));
logger.info('Screen sharing stopped.');
},
error => {
this.videoSwitchInProgress = false;
logger.error(`_turnScreenSharingOff failed: ${error}`);
throw error;
@@ -1288,13 +1421,14 @@ export default {
this._untoggleScreenSharing
= this._turnScreenSharingOff.bind(this, didHaveVideo);
const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
const desktopAudioStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
if (desktopAudioStream) {
desktopAudioStream.on(
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
() => {
logger.debug('Local screensharing audio track stopped.');
logger.debug(`Local screensharing audio track stopped. ${this.isSharingScreen}`);
// Handle case where screen share was stopped from the browsers 'screen share in progress'
// window. If audio screen sharing is stopped via the normal UX flow this point shouldn't
@@ -1306,6 +1440,21 @@ export default {
);
}
if (desktopVideoStream) {
desktopVideoStream.on(
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
() => {
logger.debug(`Local screensharing track stopped. ${this.isSharingScreen}`);
// If the stream was stopped during screen sharing
// session then we should switch back to video.
this.isSharingScreen
&& this._untoggleScreenSharing
&& this._untoggleScreenSharing();
}
);
}
return desktopStreams;
}, error => {
throw error;
@@ -1399,6 +1548,7 @@ export default {
}
APP.store.dispatch(localParticipantRoleChanged(role));
APP.API.notifyUserRoleChanged(id, role);
} else {
APP.store.dispatch(participantRoleChanged(id, role));
}
@@ -1453,6 +1603,10 @@ export default {
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (track, participantThatMutedUs) => {
if (participantThatMutedUs) {
APP.store.dispatch(participantMutedUs(participantThatMutedUs, track));
if (this.isSharingScreen && track.isVideoTrack()) {
logger.debug('TRACK_MUTE_CHANGED while screen sharing');
this._turnScreenSharingOff(false);
}
}
});
@@ -1664,14 +1818,39 @@ export default {
room.on(
JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
({ audio, video }) => {
APP.store.dispatch(onStartMutedPolicyChanged(audio, video));
const state = APP.store.getState();
updateTrackMuteState(state, APP.store.dispatch, true);
updateTrackMuteState(state, APP.store.dispatch, false);
APP.store.dispatch(
onStartMutedPolicyChanged(audio, video));
}
);
room.on(JitsiConferenceEvents.STARTED_MUTED, () => {
const audioMuted = room.isStartAudioMuted();
const videoMuted = room.isStartVideoMuted();
const localTracks = getLocalTracks(APP.store.getState()['features/base/tracks']);
const promises = [];
APP.store.dispatch(setAudioMuted(audioMuted));
APP.store.dispatch(setVideoMuted(videoMuted));
// Remove the tracks from the peerconnection.
for (const track of localTracks) {
// Always add the track on Safari because of a known issue where audio playout doesn't happen
// if the user joins audio and video muted, i.e., if there is no local media capture.
if (audioMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.AUDIO && !browser.isWebKitBased()) {
promises.push(this.useAudioStream(null));
}
if (videoMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.VIDEO) {
promises.push(this.useVideoStream(null));
}
}
Promise.allSettled(promises)
.then(() => {
APP.store.dispatch(showNotification({
titleKey: 'notify.mutedTitle',
descriptionKey: 'notify.muted'
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
});
});
room.on(
JitsiConferenceEvents.DATA_CHANNEL_OPENED, () => {
@@ -1709,16 +1888,6 @@ export default {
}, timeout);
}
);
room.on(JitsiConferenceEvents.PERMISSIONS_RECEIVED, p => {
const localParticipant = getLocalParticipant(APP.store.getState());
APP.store.dispatch(participantUpdated({
id: localParticipant.id,
local: true,
features: p
}));
});
},
/**
@@ -1883,7 +2052,8 @@ export default {
_initDeviceList(setDeviceListChangeHandler = false) {
const { mediaDevices } = JitsiMeetJS;
if (mediaDevices.isDeviceChangeAvailable()) {
if (mediaDevices.isDeviceListAvailable()
&& mediaDevices.isDeviceChangeAvailable()) {
if (setDeviceListChangeHandler) {
this.deviceChangeListener = devices =>
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
@@ -2108,10 +2278,8 @@ export default {
* @param {boolean} [requestFeedback=false] if user feedback should be
* @param {string} [hangupReason] the reason for leaving the meeting
* requested
* @param {boolean} [notifyOnConferenceTermination] whether to notify
* the user on conference termination
*/
hangup(requestFeedback = false, hangupReason, notifyOnConferenceTermination) {
hangup(requestFeedback = false, hangupReason) {
APP.store.dispatch(disableReceiver());
this._stopProxyConnection();
@@ -2130,7 +2298,7 @@ export default {
if (requestFeedback) {
const feedbackDialogClosed = (feedbackResult = {}) => {
if (!feedbackResult.wasDialogShown && hangupReason && notifyOnConferenceTermination) {
if (!feedbackResult.wasDialogShown && hangupReason) {
return APP.store.dispatch(
openLeaveReasonDialog(hangupReason)).then(() => feedbackResult);
}

115
config.js
View File

@@ -49,9 +49,7 @@ var config = {
bosh: 'https://jitsi-meet.example.com/' + subdir + 'http-bind',
// Websocket URL (XMPP)
websocket: 'wss://jitsi-meet.example.com/' + subdir + 'xmpp-websocket',
// websocketKeepAliveUrl: 'https://jitsi-meet.example.com/' + subdir + '_unlock',
// websocket: 'wss://jitsi-meet.example.com/' + subdir + 'xmpp-websocket',
// Whether BOSH should be preferred over WebSocket if both are configured.
// preferBosh: false,
@@ -89,9 +87,6 @@ var config = {
// Enables use of getDisplayMedia in electron
// electronUseGetDisplayMedia: false,
// Enables AV1 codec for FF. Note: By default it is disabled.
// enableAV1ForFF: false,
// Enables the use of the codec selection API supported by the browsers .
// enableCodecSelectionAPI: false,
@@ -117,11 +112,6 @@ var config = {
// Will replace ice candidates IPs with invalid ones in order to fail ice.
// failICE: true,
// When running on Spot TV, this controls whether to show the recording consent dialog.
// If false (default), Spot instances will not show the recording consent dialog.
// If true, Spot instances will show the recording consent dialog like regular clients.
// showSpotConsentDialog: false,
},
// Disables moderator indicators.
@@ -133,9 +123,6 @@ var config = {
// Disables the reactions moderation feature.
// disableReactionsModeration: false,
// Disables the reactions in chat feature.
// disableReactionsInChat: false,
// Disables polls feature.
// disablePolls: false,
@@ -406,13 +393,6 @@ var config = {
// // showPrejoinWarning: true,
// // If true, the notification for recording start will display a link to download the cloud recording.
// // showRecordingLink: true,
// // If true, mutes audio and video when a recording begins and displays a dialog
// // explaining the effect of unmuting.
// // requireConsent: true,
// // If true consent will be skipped for users who are already in the meeting.
// // skipConsentInMeeting: true,
// // Link for the recording consent dialog's "Learn more" link.
// // consentLearnMoreLink: 'https://jitsi.org/meet/consent',
// },
// recordingService: {
@@ -510,15 +490,6 @@ var config = {
// // Enables automatic request of subtitles when transcriber is present in the meeting, uses the default
// // language that is set
// autoCaptionOnTranscribe: false,
//
// // Disables everything related to closed captions - the tab in the chat area, the button in the menu,
// // subtitles on stage and the "Show subtitles on stage" checkbox in the settings.
// // Note: Starting transcriptions from the recording dialog will still work.
// disableClosedCaptions: false,
// // Whether to invite jigasi when backend transcriptions are enabled (asyncTranscription is true in metadata).
// // By default, we invite it.
// inviteJigasiOnBackendTranscribing: true,
// },
// Misc
@@ -544,7 +515,7 @@ var config = {
// videoQuality: {
//
// // Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8', 'H264' ],
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264', 'AV1' ],
//
// // Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',
@@ -621,7 +592,7 @@ var config = {
// },
//
// // Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based endpoint
// mobileCodecPreferenceOrder: [ 'VP8', 'VP9', 'H264', 'AV1' ],
// mobileCodecPreferenceOrder: [ 'VP8', 'VP9', 'H264' ],
// },
// Notification timeouts
@@ -630,7 +601,6 @@ var config = {
// medium: 5000,
// long: 10000,
// extraLong: 60000,
// sticky: 0,
// },
// // Options for the recording limit notification.
@@ -782,9 +752,6 @@ var config = {
// and microsoftApiApplicationClientID
// enableCalendarIntegration: false,
// Whether to notify when the conference is terminated because it was destroyed.
// notifyOnConferenceDestruction: true,
// The client id for the google APIs used for the calendar integration, youtube livestreaming, etc.
// googleApiApplicationClientID: '<client_id>',
@@ -848,7 +815,8 @@ var config = {
// some other values in config.js to be enabled. Also, the "profile" button will
// not display for users with a JWT.
// Notes:
// - it's possible to reorder the buttons in the maintoolbar by changing the order of the mainToolbarButtons
// - it's impossible to choose which buttons go in the "More actions" menu
// - it's impossible to control the placement of buttons
// - 'desktop' controls the "Share your screen" button
// - if `toolbarButtons` is undefined, we fallback to enabling all buttons on the UI
// toolbarButtons: [
@@ -1099,10 +1067,10 @@ var config = {
// Provides a way to set the codec preference on mobile devices, both on RN and mobile browser based
// endpoints.
// mobileCodecPreferenceOrder: [ 'H264', 'VP8', 'VP9', 'AV1' ],
// mobileCodecPreferenceOrder: [ 'H264', 'VP8', 'VP9' ],
//
// Provides a way to set the codec preference on desktop based endpoints.
// codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8', 'H264 ],
// codecPreferenceOrder: [ 'VP9', 'VP8', 'H264 ],
// Provides a way to set the codec for screenshare.
// screenshareCodec: 'AV1',
@@ -1131,6 +1099,10 @@ var config = {
// The Amplitude APP Key:
// amplitudeAPPKey: '<APP_KEY>',
// Enables Amplitude UTM tracking:
// Default value is false.
// amplitudeIncludeUTM: false,
// Obfuscates room name sent to analytics (amplitude, rtcstats)
// Default value is false.
// obfuscateRoomName: false,
@@ -1274,6 +1246,9 @@ var config = {
// disableDeepLinking: false,
// The deeplinking config.
// For information about the properties of
// deeplinking.[ios/android].dynamicLink check:
// https://firebase.google.com/docs/dynamic-links/create-manually
// deeplinking: {
//
// // The desktop deeplinking config, disabled by default.
@@ -1302,6 +1277,13 @@ var config = {
// appScheme: 'org.jitsi.meet',
// // Custom URL for downloading ios mobile app.
// downloadLink: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905',
// dynamicLink: {
// apn: 'org.jitsi.meet',
// appCode: 'w2atb',
// customDomain: undefined,
// ibi: 'com.atlassian.JitsiMeet.ios',
// isi: '1165103905'
// }
// },
// // The android deeplinking config.
@@ -1314,6 +1296,13 @@ var config = {
// // Android app package name.
// appPackage: 'org.jitsi.meet',
// fDroidUrl: 'https://f-droid.org/en/packages/org.jitsi.meet/',
// dynamicLink: {
// apn: 'org.jitsi.meet',
// appCode: 'w2atb',
// customDomain: undefined,
// ibi: 'com.atlassian.JitsiMeet.ios',
// isi: '1165103905'
// }
// }
// },
@@ -1361,11 +1350,18 @@ var config = {
// disableKick: true,
// // If set to true the 'Grant moderator' button will be disabled.
// disableGrantModerator: true,
// // If set to 'all' the 'Private chat' button will be disabled for all participants.
// // If set to 'allow-moderator-chat' the 'Private chat' button will be available for chats with moderators.
// disablePrivateChat: 'all' | 'allow-moderator-chat',
// // If set to true the 'Send private message' button will be disabled.
// disablePrivateChat: true,
// },
// Endpoint that enables support for salesforce integration with in-meeting resource linking
// This is required for:
// listing the most recent records - salesforceUrl/records/recents
// searching records - salesforceUrl/records?text=${text}
// retrieving record details - salesforceUrl/records/${id}?type=${type}
// and linking the meeting - salesforceUrl/sessions/${sessionId}/records/${id}
//
// salesforceUrl: 'https://api.example.com/',
// If set to true all muting operations of remote participants will be disabled.
// disableRemoteMute: true,
@@ -1376,12 +1372,8 @@ var config = {
The config file should be in JSON.
None of the fields are mandatory and the response must have the shape:
{
// Whether participant can only send group chat message if `send-groupchat` feature is enabled in jwt.
groupChatRequiresPermission: false,
// Whether participant can only create polls if `create-polls` feature is enabled in jwt.
pollCreationRequiresPermission: false,
// The domain url to apply (will replace the domain in the sharing conference link/embed section)
inviteDomain: 'example-company.org',
inviteDomain: 'example-company.org,
// The hex value for the colour used as background
backgroundColor: '#fff',
// The url for the image used as background
@@ -1390,13 +1382,6 @@ var config = {
logoClickUrl: 'https://example-company.org',
// The url used for the image used as logo
logoImageUrl: 'https://example.com/logo-img.png',
// Endpoint that enables support for salesforce integration with in-meeting resource linking
// This is required for:
// listing the most recent records - salesforceUrl/records/recents
// searching records - salesforceUrl/records?text=${text}
// retrieving record details - salesforceUrl/records/${id}?type=${type}
// and linking the meeting - salesforceUrl/sessions/${sessionId}/records/${id}
// salesforceUrl: 'https://api.example.com/',
// Overwrite for pool of background images for avatars
avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'],
// The lobby/prejoin screen background
@@ -1576,11 +1561,6 @@ var config = {
// You can enable tokenAuthUrlAutoRedirect which will detect that you have logged in successfully before
// and will automatically redirect to the token service to get the token for the meeting.
// tokenAuthUrlAutoRedirect: false
// An option to respect the context.tenant jwt field compared to the current tenant from the url
// tokenRespectTenant: false,
// An option to get for user info (name, picture, email) in the token outside the user context.
// Can be used with Firebase tokens.
// tokenGetUserInfoOutOfContext: false,
// You can put an array of values to target different entity types in the invite dialog.
// Valid values are "phone", "room", "sip", "user", "videosipgw" and "email"
@@ -1780,13 +1760,6 @@ var config = {
// // The minimum number of participants that must be in the call for
// // the top panel layout to be used.
// minParticipantCountForTopPanel: 50,
// // The width of the filmstrip on joining meeting. Can be resized afterwards.
// initialWidth: 400,
// // Whether the draggable resize bar of the filmstrip is always visible. Setting this to true will make
// // the filmstrip always visible in case `disableResizable` is false.
// alwaysShowResizeBar: true,
// },
// Tile view related config options.
@@ -1888,16 +1861,6 @@ var config = {
// If true remove the tint foreground on focused user camera in filmstrip
// disableCameraTintForeground: false,
// File sharign service.
// fileSharing: {
// // The URL of the file sharing service API. See resources/file-sharing.yaml for more details.
// apiUrl: 'https://example.com',
// // Whether the file sharing service is enabled or not.
// enabled: true,
// // Maximum file size limit (-1 value disables any file size limit check)
// maxFileSize: 50,
// },
};
// Set the default values for JaaS customers

View File

@@ -4,12 +4,12 @@
text-align: center;
h2 {
font-size: 3rem;
font-size: 48px;
color : #f2f2f2;
}
&__message {
font-size: 1.5rem;
font-size: 24px;
margin-top: 20px;
}
}

View File

@@ -28,7 +28,7 @@ body {
margin: 0px;
width: 100%;
height: 100%;
font-size: 0.75rem;
font-size: 12px;
font-weight: 400;
overflow: hidden;
color: #F1F1F1;
@@ -111,7 +111,7 @@ form {
height: $watermarkHeight;
background-size: contain;
background-repeat: no-repeat;
z-index: $watermarkZ;
z-index: $zindex2;
}
.leftwatermark {
@@ -139,10 +139,10 @@ form {
position: absolute;
left: 25;
bottom: 7;
font-size: 0.875rem;
font-size: 11pt;
color: rgba(255,255,255,.50);
text-decoration: none;
z-index: $watermarkZ;
z-index: 100;
}
/**

View File

@@ -11,9 +11,9 @@
#chatconversation {
box-sizing: border-box;
flex: 1;
font-size: 0.75rem;
font-size: 10pt;
height: calc(100% - 10px);
line-height: 1.25rem;
line-height: 20px;
overflow: auto;
padding: 16px;
text-align: left;
@@ -72,7 +72,7 @@
#nickname {
text-align: center;
color: #9d9d9d;
font-size: 1rem;
font-size: 16px;
margin: auto 0;
padding: 0 16px;
@@ -86,7 +86,7 @@
}
label {
line-height: 1.5rem;
line-height: 24px;
}
}
@@ -98,7 +98,7 @@
}
.chatmessage .usermessage {
font-size: 1rem;
font-size: 16px;
}
}
@@ -124,7 +124,7 @@
}
#smileys {
font-size: 1.625rem;
font-size: 20pt;
margin: auto;
cursor: pointer;
}
@@ -141,8 +141,34 @@
left: 0;
}
.smileys-panel {
bottom: 100%;
box-sizing: border-box;
background-color: rgba(0, 0, 0, .6) !important;
height: auto;
display: flex;
overflow: hidden;
position: absolute;
width: calc(#{$sidebarWidth} - 32px);
margin-bottom: 5px;
margin-left: -5px;
/**
* CSS transitions do not apply for auto dimensions. So to produce the css
* accordion effect for showing and hiding the smiley-panel, while allowing
* for variable panel, height, use a very large max-height and animate off
* of that.
*/
transition: max-height 0.3s;
#smileysContainer {
background-color: $chatBackgroundColor;
border-top: 1px solid #A4B8D1;
}
}
#smileysContainer .smiley {
font-size: 1.625rem;
font-size: 20pt;
}
.smileyContainer {
@@ -193,8 +219,8 @@
box-sizing: border-box;
color: #fff;
font-weight: 600;
font-size: 1.5rem;
line-height: 2rem;
font-size: 24px;
line-height: 32px;
.jitsi-icon {
cursor: pointer;

View File

@@ -34,8 +34,8 @@
}
&__checkbox-label {
font-size: 0.875rem;
line-height: 1.125rem;
font-size: 14px;
line-height: 18px;
display: flex;
align-items: center;
letter-spacing: -0.006em;
@@ -51,8 +51,8 @@
}
&__text-container {
font-size: 0.875rem;
line-height: 1.125rem;
font-size: 14px;
line-height: 18px;
display: flex;
align-items: center;
letter-spacing: -0.006em;
@@ -84,8 +84,8 @@
&__button-text {
font-weight: 600;
font-size: 0.875rem;
line-height: 2.5rem;
font-size: 14px;
line-height: 40px;
text-align: center;
letter-spacing: -0.006em;
color: #FFFFFF;

View File

@@ -10,7 +10,7 @@
margin: 17px 0;
padding-bottom: 17px;
color: #ffffff;
font-size: 1.25rem;
font-size: 21px;
letter-spacing: 0.3px;
border-bottom: 1px solid lighten(#FFFFFF, 10%);
}
@@ -19,12 +19,12 @@
color: #ffffff;
display: block;
margin-top: 22px;
font-size: 1rem;
font-size: 16px;
}
&__icon {
margin: 0 10px;
font-size: 3.125rem;
font-size: 50px;
}
}

View File

@@ -1,14 +1,14 @@
.meetings-list {
font-size: 0.875rem;
font-size: 14px;
color: #253858;
line-height: 1.25rem;
line-height: 20px;
text-align: left;
text-overflow: ellipsis;
display: flex;
flex-direction: column;
position: relative;
overflow-y: auto;
flex-grow: 1;
overflow: auto;
width: 100%;
.meetings-list-empty {
text-align: center;
@@ -20,8 +20,8 @@
.description {
color: #2f3237;
font-size: 0.875rem;
line-height: 1.125rem;
font-size: 14px;
line-height: 18px;
margin-bottom: 16px;
max-width: 436px;
}
@@ -37,8 +37,8 @@
color: #0163FF;
cursor: pointer;
display: flex;
font-size: 0.875rem;
line-height: 1.125rem;
font-size: 14px;
line-height: 18px;
margin: 24px 0 32px 0;
}
@@ -101,17 +101,17 @@
}
.title {
font-size: 0.75rem;
font-size: 12px;
font-weight: 600;
line-height: 1rem;
line-height: 16px;
margin-bottom: 4px;
}
.subtitle {
color: #5E6D7A;
font-weight: normal;
font-size: 0.75rem;
line-height: 1rem;
font-size: 12px;
line-height: 16px;
}
@@ -174,12 +174,4 @@
}
}
}
@media (max-width: 1024px) { /* Targets iPads and smaller devices */
.item {
.delete-meeting {
display: block !important;
}
}
}
}

View File

@@ -1,7 +1,7 @@
%navigate-section-list-text {
width: 100%;
font-size: 0.875rem;
line-height: 1.25rem;
font-size: 14px;
line-height: 20px;
color: $welcomePageTitleColor;
text-align: left;
font-family: 'open_sanslight', Helvetica, sans-serif;
@@ -52,7 +52,7 @@
.navigate-section-tile-body {
@extend %navigate-section-list-tile-text;
font-weight: normal;
line-height: 1.5rem;
line-height: 24px;
}
.navigate-section-list-tile-info {
flex: 1;
@@ -61,7 +61,7 @@
.navigate-section-tile-title {
@extend %navigate-section-list-tile-text;
font-weight: bold;
line-height: 1.5rem;
line-height: 24px;
}
.navigate-section-section-header {
@extend %navigate-section-list-text;

View File

@@ -4,3 +4,9 @@
border-radius: 3px;
}
}
.mobile-browser.shift-right {
.participants_pane {
z-index: -1;
}
}

View File

@@ -8,8 +8,8 @@
&__text {
text-align: center;
font-size: 0.875rem;
line-height: 1.25rem;
font-size: 14px;
line-height: 21px;
font-weight: 300;
}
}

View File

@@ -55,7 +55,7 @@
span.emoji {
width: 40px;
height: 40px;
font-size: 1.375rem;
font-size: 22px;
display: flex;
align-items: center;
justify-content: center;
@@ -63,7 +63,7 @@
@for $i from 1 through 12 {
&.increase-#{$i}{
font-size: calc(1.25rem + #{$i}px);
font-size: calc(20px + #{$i}px);
}
}
}
@@ -96,8 +96,8 @@
span.text {
font-style: normal;
font-weight: 600;
font-size: 0.875rem;
line-height: 1.5rem;
font-size: 14px;
line-height: 24px;
margin-left: 8px;
}
}
@@ -132,8 +132,8 @@ $reactionCount: 20;
.reaction-emoji {
position: absolute;
font-size: 1.5rem;
line-height: 2rem;
font-size: 24px;
line-height: 32px;
width: 32px;
height: 32px;
top: 0;

View File

@@ -12,7 +12,7 @@
.recording-title {
display: inline-flex;
align-items: center;
font-size: 0.875rem;
font-size: 14px;
margin-left: 16px;
max-width: 70%;
@@ -35,8 +35,8 @@
.local-recording-warning {
margin-top: 8px;
display: block;
font-size: 0.875rem;
line-height: 1.25rem;
font-size: 14px;
line-height: 20px;
padding: 8px 16px;
&.text {
@@ -126,7 +126,7 @@
.recording-info-title {
display: inline-flex;
font-size: 0.875rem;
font-size: 14px;
width: 290px
}
@@ -150,7 +150,7 @@
/**
* Set font-size to be consistent with Atlaskit FieldText.
*/
font-size: 0.875rem;
font-size: 14px;
.broadcast-dropdown {
text-align: left;
@@ -194,6 +194,6 @@
.warning-text {
color:#FFD740;
font-size: 0.75rem;
font-size: 12px;
}
}

View File

@@ -2,7 +2,7 @@
width: 30%;
margin: 20% auto;
text-align: center;
font-size: 1.5rem;
font-size: 24px;
.thanks-msg {
border-bottom: 1px solid #FFFFFF;
@@ -10,16 +10,16 @@
padding-right: 30px;
p {
margin: 30px auto;
font-size: 1.5rem;
line-height: 1.5rem;
font-size: 24px;
line-height: 24px;
}
}
.hint-msg {
p {
margin: 26px auto;
font-weight: 600;
font-size: 1rem;
line-height: 1.125rem;
font-size: 16px;
line-height: 18px;
.hint-msg__holder{
font-weight: 200;
}
@@ -33,7 +33,7 @@
}
.forbidden-msg {
p {
font-size: 1rem;
font-size: 16px;
margin-top: 15px;
}
}

View File

@@ -70,7 +70,7 @@ input[type="reset"] {
body {
color: #333;
font-family: Arial, sans-serif;
font-size: 0.875rem;
font-size: 14px;
line-height: 1.42857142857143;
}
/* International Font Stacks*/
@@ -113,7 +113,7 @@ pre:first-child {
/* Headings: desired line height in px / font size = unitless line height */
h1 {
color: #333;
font-size: 2rem;
font-size: 32px;
font-weight: normal;
line-height: 1.25;
text-transform: none;
@@ -121,7 +121,7 @@ h1 {
}
h2 {
color: #333;
font-size: 1.5rem;
font-size: 24px;
font-weight: normal;
line-height: 1.25;
text-transform: none;
@@ -129,14 +129,14 @@ h2 {
}
h3 {
color: #333;
font-size: 1.25rem;
font-size: 20px;
font-weight: normal;
line-height: 1.5;
text-transform: none;
margin: 30px 0 0 0;
}
h4 {
font-size: 1rem;
font-size: 16px;
font-weight: bold;
line-height: 1.25;
text-transform: none;
@@ -144,7 +144,7 @@ h4 {
}
h5 {
color: #333;
font-size: 0.875rem;
font-size: 14px;
font-weight: bold;
line-height: 1.42857143;
text-transform: none;
@@ -152,7 +152,7 @@ h5 {
}
h6 {
color: #707070;
font-size: 0.75rem;
font-size: 12px;
font-weight: bold;
line-height: 1.66666667;
text-transform: uppercase;
@@ -179,7 +179,7 @@ h5 + h6 {
/* Other typographical elements */
small {
color: #707070;
font-size: 0.75rem;
font-size: 12px;
line-height: 1.33333333333333;
}
code,

View File

@@ -4,7 +4,7 @@
#enter_room {
.welcome-page-button {
font-size: 1rem;
font-size: 16px;
left: 0;
text-align: center;
width: 100%;
@@ -53,10 +53,25 @@
}
.welcome-footer-row-block {
display: flex;
flex-direction: column;
gap:12px;
align-items: center;
display: block;
}
}
}
.desktop-browser {
&.shift-right {
@media only screen and (max-width: $verySmallScreen + $sidebarWidth) {
#videoResolutionLabel {
display: none;
}
.vertical-filmstrip .filmstrip {
display: none;
}
.chrome-extension-banner {
display: none;
}
}
}
}

View File

@@ -9,9 +9,9 @@
// Do not inherit the font-family from the toolbar button, because it's an
// icon style.
font-family: $baseFontFamily;
font-size: 0.5rem;
font-size: 9px;
font-weight: 700;
line-height: 0.75rem;
line-height: 13px;
min-width: 13px;
overflow: hidden;
text-align: center;
@@ -69,8 +69,8 @@
.badge-round {
bottom: -5px;
font-size: 0.75rem;
line-height: 1.25rem;
font-size: 12px;
line-height: 20px;
min-width: 20px;
pointer-events: none;
position: absolute;

View File

@@ -22,6 +22,7 @@ $newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
* Chat
*/
$chatBackgroundColor: #131519;
$sidebarWidth: 315px;
/**
* Misc.
@@ -37,8 +38,6 @@ $zindex1: 1;
$zindex2: 2;
$zindex3: 3;
$toolbarZ: 250;
$watermarkZ: 253;
// Place filmstrip videos over toolbar in order
// to make connection info visible.
$filmstripVideosZ: $toolbarZ + 1;
@@ -49,11 +48,11 @@ $filmstripVideosZ: $toolbarZ + 1;
$primaryUnsupportedBrowserButtonBgColor: #0052CC;
$unsupportedBrowserButtonBgColor: rgba(9, 30, 66, 0.04);
$unsupportedBrowserTextColor: #4a4a4a;
$unsupportedBrowserTextSmallFontSize: 1rem;
$unsupportedBrowserTextSmallFontSize: 17px;
$unsupportedBrowserTitleColor: #fff;
$unsupportedBrowserTitleFontSize: 1.5rem;
$unsupportedBrowserTitleFontSize: 24px;
$unsupportedDesktopBrowserTextColor: rgba(255, 255, 255, 0.7);
$unsupportedDesktopBrowserTextFontSize: 1.25rem;
$unsupportedDesktopBrowserTextFontSize: 21px;
/**
* The size of the default watermark.
@@ -76,19 +75,16 @@ $welcomePageHeaderBackground: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0,
$welcomePageHeaderBackgroundPosition: center;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: cover;
$welcomePageHeaderPadding: 1rem;
$welcomePageHeaderPaddingBottom: 15px;
$welcomePageHeaderTitleMaxWidth: initial;
$welcomePageHeaderTextAlign: center;
$welcomePageButtonBg: #0074E0;
$welcomePageButtonHoverBg: #4687ED;
$welcomePageButtonFocusOutline: #00225A;
$welcomePageHeaderContainerMarginTop: 104px;
$welcomePageHeaderContainerDisplay: flex;
$welcomePageHeaderContainerMargin: $welcomePageHeaderContainerMarginTop auto 0;
$welcomePageHeaderTextTitleMarginBottom: 0;
$welcomePageHeaderTextTitleFontSize: 2.625rem;
$welcomePageHeaderTextTitleFontSize: 42px;
$welcomePageHeaderTextTitleFontWeight: normal;
$welcomePageHeaderTextTitleLineHeight: 50px;
$welcomePageHeaderTextTitleOpacity: 1;

View File

@@ -160,7 +160,7 @@
}
#alwaysOnTop .displayname {
font-size: 0.875rem;
font-size: 15px;
position: inherit;
width: 100%;
left: 0px;
@@ -294,7 +294,7 @@
width: auto;
z-index: $zindex2;
font-weight: 600;
font-size: 0.875rem;
font-size: 14px;
text-align: center;
color: #FFF;
left: 50%;
@@ -340,7 +340,7 @@
.presence-label {
color: #fff;
font-size: 0.75rem;
font-size: 12px;
font-weight: 100;
left: 0;
margin: 0 auto;

View File

@@ -18,7 +18,7 @@ body.welcome-page {
background-position: $welcomePageHeaderBackgroundPosition;
background-repeat: $welcomePageHeaderBackgroundRepeat;
background-size: $welcomePageHeaderBackgroundSize;
padding: $welcomePageHeaderPadding;
padding-bottom: $welcomePageHeaderPaddingBottom;
background-color: #131519;
overflow: hidden;
position: relative;
@@ -53,9 +53,9 @@ body.welcome-page {
.header-text-subtitle {
color: #fff;
font-size: 1.25rem;
font-size: 20px;
font-weight: 600;
line-height: 1.625rem;
line-height: 26px;
margin: 16px 0 32px 0;
text-align: $welcomePageHeaderTextAlign;
@@ -64,7 +64,7 @@ body.welcome-page {
.not-allow-title-character-div {
color: #f03e3e;
background-color: #fff;
font-size: 0.75rem;
font-size: 12px;
font-weight: 600;
margin: 10px 0px 5px 0px;
text-align: $welcomePageHeaderTextAlign;
@@ -147,7 +147,7 @@ body.welcome-page {
display: inline-block;
height: 50px;
width: 100%;
font-size: 0.875rem;
font-size: 14px;
padding-left: 10px;
&.focus-visible {
@@ -172,7 +172,7 @@ body.welcome-page {
}
.tab-container {
font-size: 1rem;
font-size: 16px;
position: relative;
text-align: left;
display: $welcomePageTabContainerDisplay;
@@ -191,8 +191,8 @@ body.welcome-page {
background-color: #c7ddff;
border-radius: 6px;
color: #0163FF;
font-size: 0.875rem;
line-height: 1.125rem;
font-size: 14px;
line-height: 18px;
margin: 4px;
display: $welcomePageTabButtonsDisplay;
@@ -218,19 +218,15 @@ body.welcome-page {
.welcome-page-button {
border: 0;
font-size: 0.875rem;
background: $welcomePageButtonBg;
font-size: 14px;
background: #0074E0;
border-radius: 3px;
color: #FFFFFF;
cursor: pointer;
padding: 16px 20px;
transition: all 0.2s;
&:focus-within {
outline: auto 2px $welcomePageButtonFocusOutline;
}
&:hover {
background-color: $welcomePageButtonHoverBg;
&:focus-within {
outline: auto 2px #022e61;
}
}
@@ -246,7 +242,7 @@ body.welcome-page {
* {
cursor: pointer;
font-size: 2rem;
font-size: 32px;
}
.toolbox-icon {
@@ -268,7 +264,8 @@ body.welcome-page {
&.without-content {
.welcome-card {
max-width: 100dvw;
min-width: 500px;
max-width: 580px;
}
}
@@ -349,6 +346,6 @@ body.welcome-page {
.welcome-footer-row-1-text {
max-width: 200px;
text-align: center;
margin-right: 16px;
}
}

View File

@@ -91,3 +91,15 @@
}
}
}
.shift-right .remote-videos > div {
/**
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants,
* from which we subtract the chat size.
*/
@media only screen and (max-width: calc(500px + #{$sidebarWidth})) {
video {
object-fit: cover;
}
}
}

View File

@@ -20,7 +20,7 @@
width: 28px;
i {
line-height: 1.75rem;
line-height: 28px;
margin: auto;
}
}
@@ -35,7 +35,7 @@
padding-left: 10px;
i {
line-height: 1.25rem;
line-height: 20px;
margin: auto;
}
}

View File

@@ -1,7 +1,7 @@
.info-dialog {
cursor: default;
display: flex;
font-size: 0.875rem;
font-size: 14px;
.info-dialog-column {
margin-right: 10px;
@@ -53,8 +53,8 @@
max-width: 334px;
width: 100%;
margin-top: 20px;
font-size: 0.75rem;
line-height: 1.5rem;
font-size: 12px;
line-height: 24px;
border-collapse: collapse;
* {
@@ -107,7 +107,7 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
font-size: 0.75rem;
font-size: 12px;
max-height: 100%;
overflow: auto;
padding: 15pt;

View File

@@ -1,8 +1,8 @@
.invite-more {
&-dialog {
color: #fff;
font-size: 0.875rem;
line-height: 1.5rem;
font-size: 15px;
line-height: 24px;
&.separator {
margin: 24px 0 24px -20px;

View File

@@ -1,5 +1,5 @@
.share-screen-warn-dialog {
font-size: 0.875rem;
font-size: 14px;
.separator-line {
margin: 24px 0 24px -20px;

View File

@@ -1,15 +1,15 @@
.security {
&-dialog {
color: #fff;
font-size: 0.875rem;
line-height: 1.5rem;
font-size: 15px;
line-height: 24px;
&.password-section {
display: flex;
flex-direction: column;
.description {
font-size: 0.75rem;
font-size: 13px;
}
.password {
@@ -24,7 +24,7 @@
button {
cursor: pointer;
text-decoration: none;
font-size: 0.875rem;
font-size: 14px;
color: #6FB1EA;
}

View File

@@ -1,7 +1,7 @@
.lobby-screen {
font-size: 1rem;
font-size: 16px;
font-weight: 400;
line-height: 1.625rem;
line-height: 26px;
&-content {
align-items: center;
@@ -43,7 +43,7 @@
flex-direction: column;
.description {
font-size: 0.75rem;
font-size: 13px;
}
.control-row {
@@ -53,7 +53,7 @@
margin-top: 15px;
label {
font-size: 0.875rem;
font-size: 14px;
font-weight: bold;
}
}
@@ -191,9 +191,9 @@
.title {
flex: 1;
color: #fff;
font-size: 1.25rem;
font-size: 20px;
font-weight: 600;
line-height: 1.75rem;
line-height: 28px;
letter-spacing: -1.2%;
}
}
@@ -214,8 +214,8 @@
border-radius: 6px;
box-sizing: border-box;
color: white;
font-size: 0.75rem;
line-height: 1rem;
font-size: 12px;
line-height: 16px;
margin-bottom: 16px;
margin-top: -8px;
padding: 4px;

View File

@@ -5,9 +5,9 @@
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 0.875rem;
font-size: 14px;
font-weight: 600;
line-height: 1.5rem;
line-height: 24px;
margin-bottom: 16px;
padding: 7px 16px;
position: relative;
@@ -26,7 +26,7 @@
&.text {
width: auto;
font-size: 0.75rem;
font-size: 13px;
margin: 0;
padding: 0;
}
@@ -98,7 +98,7 @@
}
.action-btn {
font-size: 1rem;
font-size: 16px;
margin-bottom: 8px;
padding: 11px 16px;
}

View File

@@ -1,13 +1,13 @@
.reload_overlay_title {
display: block;
font-size: 1rem;
line-height: 1.25rem;
font-size: 16px;
line-height: 20px;
}
.reload_overlay_text {
display: block;
font-size: 0.75rem;
line-height: 1.875rem;
font-size: 12px;
line-height: 30px;
}
#reloadProgressBar {

View File

@@ -34,12 +34,12 @@
&__status{
margin-top: 15px;
font-size: 0.875rem;
line-height: 1.25rem;
font-size: 14px;
line-height: 20px;
}
&__name {
font-size: 1.5rem;
line-height: 2rem;
font-size: 24px;
line-height: 32px;
}
}

View File

@@ -8,7 +8,7 @@
cursor: pointer;
display: inline-flex;
font-family: Roboto, arial, sans-serif;
font-size: 0.875rem;
font-size: 14px;
padding: 1px;
.google-cta {
@@ -17,7 +17,7 @@
/**
* Hack the line height for vertical centering of text.
*/
line-height: 2rem;
line-height: 32px;
margin: 0 15px;
}

View File

@@ -17,8 +17,8 @@
.microsoft-cta {
display: inline-block;
color: #5E5E5E;
font-size: 0.875rem;
line-height: 2.5rem;
font-size: 15px;
line-height: 41px;
}
.microsoft-logo {

4
debian/control vendored
View File

@@ -34,7 +34,7 @@ Description: Configuration for web serving of Jitsi Meet
Package: jitsi-meet-prosody
Architecture: all
Depends: openssl, prosody (>= 0.12.0) | prosody-trunk | prosody-0.12 | prosody-13.0, lua-sec, lua-basexx, lua-luaossl, lua-cjson, lua-inspect
Depends: openssl, prosody (>= 0.11.7) | prosody-trunk | prosody-0.12 | prosody-0.11, lua-sec, lua-basexx, lua-luaossl, lua-cjson, lua-inspect
Replaces: jitsi-meet-tokens
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
@@ -48,7 +48,7 @@ Description: Prosody configuration for Jitsi Meet
Package: jitsi-meet-tokens
Architecture: all
Depends: ${misc:Depends}, prosody-trunk | prosody-0.12 | prosody-13.0 | prosody (>= 0.12.0), jitsi-meet-prosody
Depends: ${misc:Depends}, prosody-trunk | prosody-0.11 | prosody-0.12 | prosody (>= 0.11.7), jitsi-meet-prosody
Description: Prosody token authentication plugin for Jitsi Meet
Package: jitsi-meet-turnserver

View File

@@ -131,6 +131,16 @@ case "$1" in
fi
fi
if [ "$PROSODY_CREATE_JICOFO_USER" = "true" ]; then
# create 'focus@auth.domain' prosody user
prosodyctl register $JICOFO_AUTH_USER $JICOFO_AUTH_DOMAIN $JICOFO_AUTH_PASSWORD
# trigger a restart
PROSODY_CONFIG_PRESENT="false"
fi
# creates the user if it does not exist
echo -e "$JVB_SECRET\n$JVB_SECRET" | prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN > /dev/null || true
# Check whether prosody config has the internal muc, if not add it,
# as we are migrating configs
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.$JICOFO_AUTH_DOMAIN" $PROSODY_HOST_CONFIG; then
@@ -174,12 +184,6 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
# Since prosody 13 admins are not automatically room owners and we expect that for jicofo
if ! grep -q -- 'component_admins_as_room_owners = ' $PROSODY_HOST_CONFIG ;then
sed -i "1s/^/component_admins_as_room_owners = true\n/" $PROSODY_HOST_CONFIG
PROSODY_CONFIG_PRESENT="false"
fi
JAAS_HOST_CONFIG="/etc/prosody/conf.avail/jaas.cfg.lua"
if [ "${JAAS_INPUT}" = "true" ] && [ ! -f $JAAS_HOST_CONFIG ]; then
sed -i "s/enabled = false -- Jitsi meet components/enabled = true -- Jitsi meet components/g" $PROSODY_HOST_CONFIG
@@ -203,6 +207,9 @@ case "$1" in
fi
fi
# Make sure the focus@auth user's roster includes the proxy component (this is idempotent)
prosodyctl mod_roster_command subscribe focus.$JVB_HOSTNAME $JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
# prosodyctl takes care for the permissions
# echo for using all default values
@@ -245,29 +252,6 @@ case "$1" in
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
invoke-rc.d prosody restart || true
# give it some time to warm up
sleep 10
if [ "$PROSODY_CREATE_JICOFO_USER" = "true" ]; then
# create 'focus@auth.domain' prosody user
echo -e "$JICOFO_AUTH_PASSWORD\n$JICOFO_AUTH_PASSWORD" | prosodyctl adduser $JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN > /dev/null || true
# trigger a restart
PROSODY_CONFIG_PRESENT="false"
fi
# creates the user if it does not exist
echo -e "$JVB_SECRET\n$JVB_SECRET" | prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN > /dev/null || true
# Make sure the focus@auth user's roster includes the proxy component (this is idempotent)
prosodyctl mod_roster_command subscribe focus.$JVB_HOSTNAME $JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN
# To make sure the roster command is loaded
# Once we have https://issues.prosody.im/1908 we can start using prosodyctl shell roster subscribe
# and drop the wait and the prosody restart
sleep 1
invoke-rc.d prosody restart || true
# In case we had updated the certificates and restarted prosody, let's restart and the bridge and jicofo if possible
if [ -d /run/systemd/system ] && [ "$CERT_ADDED_TO_TRUST" = "true" ]; then
systemctl restart jitsi-videobridge2.service >/dev/null || true

View File

@@ -1,6 +1,3 @@
-- We need this for prosody 13.0
component_admins_as_room_owners = true
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
-- domain mapper options, must at least have domain base set to use the mapper
@@ -15,7 +12,6 @@ external_services = {
cross_domain_bosh = false;
consider_bosh_secure = true;
consider_websocket_secure = true;
-- https_ports = { }; -- Remove this line to prevent listening on port 5284
-- by default prosody 0.12 sends cors headers, if you want to disable it uncomment the following (the config is available on 0.12.1)
@@ -39,11 +35,6 @@ unlimited_jids = {
"jvb@auth.jitmeet.example.com"
}
-- https://prosody.im/doc/modules/mod_smacks
smacks_max_unacked_stanzas = 5;
smacks_hibernation_time = 60;
smacks_max_old_sessions = 1;
VirtualHost "jitmeet.example.com"
authentication = "jitsi-anonymous" -- do not delete me
-- Properties below are modified by jitsi-meet-tokens package config
@@ -58,21 +49,26 @@ VirtualHost "jitmeet.example.com"
key = "/etc/prosody/certs/jitmeet.example.com.key";
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
}
av_moderation_component = "avmoderation.jitmeet.example.com"
speakerstats_component = "speakerstats.jitmeet.example.com"
end_conference_component = "endconference.jitmeet.example.com"
-- we need bosh
modules_enabled = {
"bosh";
"websocket";
"smacks";
"ping"; -- Enable mod_ping
"speakerstats";
"external_services";
"features_identity";
"conference_duration";
"end_conference";
"muc_lobby_rooms";
"muc_breakout_rooms";
"av_moderation";
"room_metadata";
}
c2s_require_encryption = false
lobby_muc = "lobby.jitmeet.example.com"
breakout_rooms_muc = "breakout.jitmeet.example.com"
room_metadata_component = "metadata.jitmeet.example.com"
main_muc = "conference.jitmeet.example.com"
-- muc_lobby_whitelist = { "recorder.jitmeet.example.com" } -- Here we can whitelist jibri to enter lobby enabled rooms
@@ -128,13 +124,6 @@ VirtualHost "auth.jitmeet.example.com"
authentication = "internal_hashed"
smacks_hibernation_time = 15;
VirtualHost "recorder.jitmeet.example.com"
modules_enabled = {
"smacks";
}
authentication = "internal_hashed"
smacks_max_old_sessions = 2000;
-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
Component "focus.jitmeet.example.com" "client_proxy"
target_address = "focusUser@auth.jitmeet.example.com"
@@ -148,9 +137,6 @@ Component "endconference.jitmeet.example.com" "end_conference"
Component "avmoderation.jitmeet.example.com" "av_moderation_component"
muc_component = "conference.jitmeet.example.com"
Component "filesharing.jitmeet.example.com" "filesharing_component"
muc_component = "conference.jitmeet.example.com"
Component "lobby.jitmeet.example.com" "muc"
storage = "memory"
restrict_room_creation = true

View File

@@ -150,12 +150,6 @@ server {
# alias /usr/share/jitsi-meet/load-test/libs/$1;
#}
location = /_unlock {
add_header 'Access-Control-Allow-Origin' '*';
add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains';
add_header "Cache-Control" "no-cache, no-store";
}
location ~ ^/conference-request/v1(\/.*)?$ {
proxy_pass http://127.0.0.1:8888/conference-request/v1$1;
add_header "Cache-Control" "no-cache, no-store";

View File

@@ -36,7 +36,7 @@
Component: JitsiMeetJS.app.entryPoints.APP
})
const isEmbedded = () => {
const inIframe = () => {
try {
return window.self !== window.top;
} catch (e) {
@@ -45,7 +45,7 @@
};
const isElectron = navigator.userAgent.includes('Electron');
const shouldRegisterWorker = !isElectron && !isEmbedded() && 'serviceWorker' in navigator;
const shouldRegisterWorker = !isElectron && !inIframe() && 'serviceWorker' in navigator;
if (shouldRegisterWorker) {
navigator.serviceWorker

View File

@@ -192,6 +192,17 @@ var interfaceConfig = {
// NATIVE_APP_NAME: 'Jitsi Meet',
/**
* Specify Firebase dynamic link properties for the mobile apps.
*/
// MOBILE_DYNAMIC_LINK: {
// APN: 'org.jitsi.meet',
// APP_CODE: 'w2atb',
// CUSTOM_DOMAIN: undefined,
// IBI: 'com.atlassian.JitsiMeet.ios',
// ISI: '1165103905'
// },
/**
* Hide the logo on the deep linking pages.
*/

View File

@@ -19,6 +19,7 @@ target 'JitsiMeet' do
pod 'Firebase/Analytics', '~> 8.0'
pod 'Firebase/Crashlytics', '~> 8.0'
pod 'Firebase/DynamicLinks', '~> 8.0'
end
target 'JitsiMeetSDK' do
@@ -33,7 +34,6 @@ target 'JitsiMeetSDK' do
:path => config[:reactNativePath],
:hermes_enabled => true,
:fabric_enabled => false,
:new_arch_enabled => false,
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
@@ -67,7 +67,6 @@ target 'JitsiMeetSDKLite' do
:path => config[:reactNativePath],
:hermes_enabled => true,
:fabric_enabled => false,
:new_arch_enabled => false,
# An absolute path to your application root.
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
@@ -79,12 +78,10 @@ target 'JitsiMeetSDKLite' do
end
post_install do |installer|
react_native_post_install(
installer,
use_native_modules![:reactNativePath],
:mac_catalyst_enabled => false,
# :ccache_enabled => true
:mac_catalyst_enabled => false
)
installer.pods_project.targets.each do |target|
# https://github.com/CocoaPods/CocoaPods/issues/11402
@@ -100,8 +97,4 @@ post_install do |installer|
config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -no-verify-emitted-module-interface'
end
end
# Patch SocketRocket to support TLS 1.3
%x(patch Pods/SocketRocket/SocketRocket/SRSecurityPolicy.m -N < patches/ws-tls13.diff)
end

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */; };
0B412F211EDEE95300B1A0A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B412F201EDEE95300B1A0A6 /* Main.storyboard */; };
0B5418471F7C5D8C00A2DD86 /* MeetingRowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */; };
0B7001701F7C51CC005944F4 /* InCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B70016F1F7C51CC005944F4 /* InCallController.swift */; };
0BEA5C291F7B8F73000D0AB4 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */; };
@@ -17,8 +19,10 @@
0BEA5C3B1F7B8F73000D0AB4 /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */; };
0BEA5C3D1F7B8F73000D0AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */; };
0BEA5C411F7B8F73000D0AB4 /* JitsiMeetCompanion.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.storyboard */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2681BB562C7A0B42CFBA6719 /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */; };
361974E2A13624D7735D619D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 5C1BE20ECD5DEEB48FED90B5 /* PrivacyInfo.xcprivacy */; };
4341A9062CF0D63200940D93 /* hermes.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4341A9052CF0D63200940D93 /* hermes.xcframework */; };
@@ -30,8 +34,7 @@
4EB0603C260E09D000F524C5 /* SocketConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06039260E09D000F524C5 /* SocketConnection.swift */; };
4EB0603D260E09D000F524C5 /* DarwinNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */; };
4EB0603E260E09D000F524C5 /* SampleUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603B260E09D000F524C5 /* SampleUploader.swift */; };
DEA0B7122D7EF16E0062A9F6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEA0B7112D7EF16E0062A9F6 /* ViewController.swift */; };
DEA0B7142D7EF7590062A9F6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEA0B7132D7EF7590062A9F6 /* AppDelegate.swift */; };
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */; };
DEA9F289258A6EA800D4CD74 /* JitsiMeetSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; };
DEA9F28A258A6EA800D4CD74 /* JitsiMeetSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DED016F128ECBC9D009D5E8D /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DED016F028ECBC9D009D5E8D /* WebRTC.xcframework */; };
@@ -118,8 +121,12 @@
/* Begin PBXFileReference section */
0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
0B412F201EDEE95300B1A0A6 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingRowController.swift; sourceTree = "<group>"; };
0B70016F1F7C51CC005944F4 /* InCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InCallController.swift; sourceTree = "<group>"; };
0BBD021F212EB69D00CCB19F /* Types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Types.h; sourceTree = "<group>"; };
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JitsiMeetCompanion.app; sourceTree = BUILT_PRODUCTS_DIR; };
0BEA5C281F7B8F73000D0AB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
@@ -132,9 +139,12 @@
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = src/Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
3E0F4ED943C0B12BE77F6B45 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
4341A9052CF0D63200940D93 /* hermes.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = hermes.xcframework; path = "../Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework"; sourceTree = "<group>"; };
4E90F93F2632D1AB001102D4 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
@@ -147,12 +157,13 @@
4EB0603B260E09D000F524C5 /* SampleUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUploader.swift; sourceTree = "<group>"; };
4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
5C1BE20ECD5DEEB48FED90B5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
756FCE06C08D9B947653C98A /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
D6152FF9E9F7B0E86F70A21D /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DE050388256E904600DEE3A5 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/apple/WebRTC.xcframework"; sourceTree = "<group>"; };
DEA0B7112D7EF16E0062A9F6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
DEA0B7132D7EF7590062A9F6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUtilities.m; sourceTree = "<group>"; };
DE4C456021DE1E4E00EA0709 /* FIRUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRUtilities.h; sourceTree = "<group>"; };
DEA9F288258A6EA800D4CD74 /* JitsiMeetSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JitsiMeetSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DED016F028ECBC9D009D5E8D /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = ../Pods/JitsiWebRTC/WebRTC.xcframework; sourceTree = "<group>"; };
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.xcframework"; sourceTree = "<group>"; };
@@ -247,10 +258,18 @@
13B07FAE1A68108700A75B9A /* src */ = {
isa = PBXGroup;
children = (
DEA0B7132D7EF7590062A9F6 /* AppDelegate.swift */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
DE4C456021DE1E4E00EA0709 /* FIRUtilities.h */,
DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
DEA0B7112D7EF16E0062A9F6 /* ViewController.swift */,
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
0B412F201EDEE95300B1A0A6 /* Main.storyboard */,
0BBD021F212EB69D00CCB19F /* Types.h */,
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */,
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */,
);
path = src;
sourceTree = "<group>";
@@ -283,6 +302,7 @@
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
CDD71F5E1157E9F283DF92A8 /* Pods */,
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */,
5C1BE20ECD5DEEB48FED90B5 /* PrivacyInfo.xcprivacy */,
);
indentWidth = 2;
@@ -415,7 +435,6 @@
ProvisioningStyle = Automatic;
};
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1620;
SystemCapabilities = {
com.apple.SafariKeychain = {
enabled = 1;
@@ -473,8 +492,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0B412F211EDEE95300B1A0A6 /* Main.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.storyboard in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
361974E2A13624D7735D619D /* PrivacyInfo.xcprivacy in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -538,7 +558,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#if test \"$PRODUCT_BUNDLE_IDENTIFIER\" = \"com.atlassian.JitsiMeet.ios\"; then\n# ENTITLEMENTS_PLIST=\"$PROJECT_DIR/app.entitlements\"\n# \n# /usr/libexec/PlistBuddy -c \"Add :com.apple.developer.avfoundation.multitasking-camera-access bool 1\" $ENTITLEMENTS_PLIST\n#fi\n";
shellScript = "if test \"$PRODUCT_BUNDLE_IDENTIFIER\" = \"com.atlassian.JitsiMeet.ios\"; then\n ENTITLEMENTS_PLIST=\"$PROJECT_DIR/app.entitlements\"\n \n /usr/libexec/PlistBuddy -c \"Add :com.apple.developer.avfoundation.multitasking-camera-access bool 1\" $ENTITLEMENTS_PLIST\nfi\n";
};
69BC5020DBE393B56BD76636 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
@@ -640,8 +660,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DEA0B7122D7EF16E0062A9F6 /* ViewController.swift in Sources */,
DEA0B7142D7EF7590062A9F6 /* AppDelegate.swift in Sources */,
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */,
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -686,12 +708,12 @@
name = Interface.storyboard;
sourceTree = "<group>";
};
13B07FB11A68108700A75B9A /* LaunchScreen.storyboard */ = {
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.storyboard;
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
@@ -847,7 +869,6 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -872,8 +893,6 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -884,7 +903,6 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -908,7 +926,6 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;

23
ios/app/src/AppDelegate.h Normal file
View File

@@ -0,0 +1,23 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* 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 <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

139
ios/app/src/AppDelegate.m Normal file
View File

@@ -0,0 +1,139 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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 "AppDelegate.h"
#import "FIRUtilities.h"
#import "Types.h"
#import "ViewController.h"
@import Firebase;
@import JitsiMeetSDK;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
JitsiMeet *jitsiMeet = [JitsiMeet sharedInstance];
#if 0
jitsiMeet.webRtcLoggingSeverity = WebRTCLoggingSeverityVerbose;
#endif
jitsiMeet.conferenceActivityType = JitsiMeetConferenceActivityType;
jitsiMeet.customUrlScheme = @"org.jitsi.meet";
jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"alpha.jitsi.net", @"beta.meet.jit.si"];
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
// For testing configOverrides a room needs to be set
// builder.room = @"https://meet.jit.si/test0988test";
[builder setFeatureFlag:@"welcomepage.enabled" withBoolean:YES];
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
}];
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
// Initialize Crashlytics and Firebase if a valid GoogleService-Info.plist file was provided.
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
NSLog(@"Enabling Firebase");
[FIRApp configure];
// Crashlytics defaults to disabled with the FirebaseCrashlyticsCollectionEnabled Info.plist key.
[[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:![jitsiMeet isCrashReportingDisabled]];
}
ViewController *rootController = (ViewController *)self.window.rootViewController;
[jitsiMeet showSplashScreen:rootController.view];
return YES;
}
- (void) applicationWillTerminate:(UIApplication *)application {
NSLog(@"Application will terminate!");
// Try to leave the current meeting graceefully.
ViewController *rootController = (ViewController *)self.window.rootViewController;
[rootController terminate];
}
#pragma mark Linking delegate methods
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
// 1. Attempt to handle Universal Links through Firebase in order to support
// its Dynamic Links (which we utilize for the purposes of deferred deep
// linking).
BOOL handled
= [[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
NSURL *firebaseUrl = [FIRUtilities extractURL:dynamicLink];
if (firebaseUrl != nil) {
userActivity.webpageURL = firebaseUrl;
[[JitsiMeet sharedInstance] application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
}];
if (handled) {
return handled;
}
}
// 2. Default to plain old, non-Firebase-assisted Universal Links.
return [[JitsiMeet sharedInstance] application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
// This shows up during a reload in development, skip it.
// https://github.com/firebase/firebase-ios-sdk/issues/233
if ([[url absoluteString] containsString:@"google/link/?dismiss=1&is_weak_match=1"]) {
return NO;
}
NSURL *openUrl = url;
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
// Process Firebase Dynamic Links
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
NSURL *firebaseUrl = [FIRUtilities extractURL:dynamicLink];
if (firebaseUrl != nil) {
openUrl = firebaseUrl;
}
}
return [[JitsiMeet sharedInstance] application:app
openURL:openUrl
options:options];
}
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return [[JitsiMeet sharedInstance] application:application
supportedInterfaceOrientationsForWindow:window];
}
@end

View File

@@ -1,78 +0,0 @@
import UIKit
import Firebase
import JitsiMeetSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let jitsiMeet = JitsiMeet.sharedInstance()
// jitsiMeet.webRtcLoggingSeverity = .verbose
jitsiMeet.conferenceActivityType = "org.jitsi.JitsiMeet.ios.conference" // Must match the one defined in Info.plist{}
jitsiMeet.customUrlScheme = "org.jitsi.meet"
jitsiMeet.universalLinkDomains = ["meet.jit.si", "alpha.jitsi.net", "beta.meet.jit.si"]
jitsiMeet.defaultConferenceOptions = JitsiMeetConferenceOptions.fromBuilder { builder in
// For testing configOverrides a room needs to be set
// builder.room = "https://meet.jit.si/test0988test"
builder.setFeatureFlag("welcomepage.enabled", withBoolean: true)
builder.setFeatureFlag("ios.screensharing.enabled", withBoolean: true)
builder.setFeatureFlag("ios.recording.enabled", withBoolean: true)
}
jitsiMeet.application(application, didFinishLaunchingWithOptions: launchOptions ?? [:])
if self.appContainsRealServiceInfoPlist() {
print("Enabling Firebase")
FirebaseApp.configure()
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(!jitsiMeet.isCrashReportingDisabled())
}
let vc = ViewController()
self.window?.rootViewController = vc
jitsiMeet.showSplashScreen()
self.window?.makeKeyAndVisible()
return true
}
func applicationWillTerminate(_ application: UIApplication) {
print("Application will terminate!")
if let rootController = self.window?.rootViewController as? ViewController {
rootController.terminate()
}
}
// MARK: Linking delegate methods
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
return JitsiMeet.sharedInstance().application(application, continue: userActivity, restorationHandler: restorationHandler)
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
if url.absoluteString.contains("google/link/?dismiss=1&is_weak_match=1") {
return false
}
return JitsiMeet.sharedInstance().application(app, open: url, options: options)
}
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return JitsiMeet.sharedInstance().application(application, supportedInterfaceOrientationsFor: window)
}
}
// Firebase utilities
extension AppDelegate {
func appContainsRealServiceInfoPlist() -> Bool {
return InfoPlistUtil.containsRealServiceInfoPlist(in: Bundle.main)
}
}

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" userInteractionEnabled="NO" contentMode="center" image="LaunchScreen" translatesAutoresizingMaskIntoConstraints="NO" id="4B8-Xf-NDE">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.090196078431372548" green="0.62745098039215685" blue="0.85882352941176465" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="4B8-Xf-NDE" secondAttribute="bottom" id="aFF-BR-glX"/>
<constraint firstItem="4B8-Xf-NDE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="glR-YN-1GF"/>
<constraint firstAttribute="trailing" secondItem="4B8-Xf-NDE" secondAttribute="trailing" id="tva-gl-jRX"/>
<constraint firstItem="4B8-Xf-NDE" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="yaV-1V-oEh"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<resources>
<image name="LaunchScreen" width="480" height="480"/>
</resources>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" userInteractionEnabled="NO" contentMode="center" image="LaunchScreen" translatesAutoresizingMaskIntoConstraints="NO" id="4B8-Xf-NDE">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.090196078431372548" green="0.62745098039215685" blue="0.85882352941176465" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="4B8-Xf-NDE" secondAttribute="bottom" id="aFF-BR-glX"/>
<constraint firstItem="4B8-Xf-NDE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="glR-YN-1GF"/>
<constraint firstAttribute="trailing" secondItem="4B8-Xf-NDE" secondAttribute="trailing" id="tva-gl-jRX"/>
<constraint firstItem="4B8-Xf-NDE" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="yaV-1V-oEh"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
<resources>
<image name="LaunchScreen" width="480" height="480"/>
</resources>
</document>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="u7g-vg-A8m">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="xSm-U5-Hdu">
<objects>
<viewController id="u7g-vg-A8m" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="d40-fc-Y8P"/>
<viewControllerLayoutGuide type="bottom" id="1KD-Ho-g0H"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="QpR-jB-WOw" customClass="JitsiMeetView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ZIj-K3-jVH" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="28" y="138"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2017 Google
*
* 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 <Foundation/Foundation.h>
@import Firebase;
@interface FIRUtilities : NSObject
+ (BOOL)appContainsRealServiceInfoPlist;
+ (NSURL *_Nullable)extractURL: (FIRDynamicLink* _Nullable)dynamicLink;
@end

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2017 Google
*
* 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 "FIRUtilities.h"
@import JitsiMeetSDK;
@implementation FIRUtilities
+ (BOOL)appContainsRealServiceInfoPlist {
static BOOL containsRealServiceInfoPlist = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle mainBundle];
containsRealServiceInfoPlist = [InfoPlistUtil containsRealServiceInfoPlistInBundle:bundle];
});
return containsRealServiceInfoPlist;
}
+ (NSURL *)extractURL: (FIRDynamicLink*)dynamicLink {
NSURL *url = nil;
if (dynamicLink != nil) {
NSURL *dynamicLinkURL = dynamicLink.url;
if (dynamicLinkURL != nil
&& (dynamicLink.matchType == FIRDLMatchTypeUnique
|| dynamicLink.matchType == FIRDLMatchTypeDefault)) {
// Strong match, process it.
url = dynamicLinkURL;
}
}
return url;
}
@end

View File

@@ -88,9 +88,11 @@
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
<string>armv7</string>
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>

7
ios/app/src/Types.h Normal file
View File

@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
// This must match what's defined in the NSUserActivityTypes array in the
// Info.plist file.
static NSString *const JitsiMeetConferenceActivityType
= @"org.jitsi.JitsiMeet.ios.conference";

View File

@@ -0,0 +1,24 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* 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 UIKit;
@import JitsiMeetSDK;
@interface ViewController : UIViewController<JitsiMeetViewDelegate>
- (void)terminate;
@end

View File

@@ -0,0 +1,149 @@
/*
* Copyright @ 2017-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 CoreSpotlight;
@import MobileCoreServices;
@import Intents; // Needed for NSUserActivity suggestedInvocationPhrase
@import JitsiMeetSDK;
#import "Types.h"
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
JitsiMeetView *view = (JitsiMeetView *) self.view;
view.delegate = self;
[view join:[[JitsiMeet sharedInstance] getInitialConferenceOptions]];
}
// JitsiMeetViewDelegate
- (void)_onJitsiMeetViewDelegateEvent:(NSString *)name
withData:(NSDictionary *)data {
NSLog(
@"[%s:%d] JitsiMeetViewDelegate %@ %@",
__FILE__, __LINE__, name, data);
#if DEBUG
NSAssert(
[NSThread isMainThread],
@"JitsiMeetViewDelegate %@ method invoked on a non-main thread",
name);
#endif
}
- (void)conferenceJoined:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
// Register a NSUserActivity for this conference so it can be invoked as a
// Siri shortcut.
NSUserActivity *userActivity
= [[NSUserActivity alloc] initWithActivityType:JitsiMeetConferenceActivityType];
NSString *urlStr = data[@"url"];
NSURL *url = [NSURL URLWithString:urlStr];
NSString *conference = [url.pathComponents lastObject];
userActivity.title = [NSString stringWithFormat:@"Join %@", conference];
userActivity.suggestedInvocationPhrase = @"Join my Jitsi meeting";
userActivity.userInfo = @{@"url": urlStr};
[userActivity setEligibleForSearch:YES];
[userActivity setEligibleForPrediction:YES];
[userActivity setPersistentIdentifier:urlStr];
// Subtitle
CSSearchableItemAttributeSet *attributes
= [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
attributes.contentDescription = urlStr;
userActivity.contentAttributeSet = attributes;
self.userActivity = userActivity;
[userActivity becomeCurrent];
}
- (void)conferenceTerminated:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_TERMINATED" withData:data];
}
- (void)conferenceWillJoin:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_WILL_JOIN" withData:data];
}
// - (void)customButtonPressed:(NSDictionary *)data {
// [self _onJitsiMeetViewDelegateEvent:@"CUSTOM_BUTTON_PRESSED" withData:data];
// }
#if 0
- (void)enterPictureInPicture:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"ENTER_PICTURE_IN_PICTURE" withData:data];
}
#endif
- (void)readyToClose:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"READY_TO_CLOSE" withData:data];
}
// - (void)transcriptionChunkReceived:(NSDictionary *)data {
// [self _onJitsiMeetViewDelegateEvent:@"TRANSCRIPTION_CHUNK_RECEIVED" withData:data];
// }
- (void)participantJoined:(NSDictionary *)data {
NSLog(@"%@%@", @"Participant joined: ", data[@"participantId"]);
}
- (void)participantLeft:(NSDictionary *)data {
NSLog(@"%@%@", @"Participant left: ", data[@"participantId"]);
}
- (void)audioMutedChanged:(NSDictionary *)data {
NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]);
}
- (void)endpointTextMessageReceived:(NSDictionary *)data {
NSLog(@"%@%@", @"Endpoint text message received: ", data);
}
- (void)screenShareToggled:(NSDictionary *)data {
NSLog(@"%@%@", @"Screen share toggled: ", data);
}
- (void)chatMessageReceived:(NSDictionary *)data {
NSLog(@"%@%@", @"Chat message received: ", data);
}
- (void)chatToggled:(NSDictionary *)data {
NSLog(@"%@%@", @"Chat toggled: ", data);
}
- (void)videoMutedChanged:(NSDictionary *)data {
NSLog(@"%@%@", @"Video muted changed: ", data[@"muted"]);
}
#pragma mark - Helpers
- (void)terminate {
JitsiMeetView *view = (JitsiMeetView *) self.view;
[view leave];
}
@end

View File

@@ -1,94 +0,0 @@
/*
* Copyright @ 2025-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 CoreSpotlight
import Intents
import MobileCoreServices
import UIKit
import JitsiMeetSDK
@objcMembers
class ViewController: UIViewController {
override func loadView() {
let jitsiView = JitsiMeetView(frame: UIScreen.main.bounds)
self.view = jitsiView
}
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as? JitsiMeetView else { return }
view.delegate = self
view.join(JitsiMeet.sharedInstance().getInitialConferenceOptions())
}
// MARK: - Helper Methods
func terminate() {
guard let view = self.view as? JitsiMeetView else { return }
view.leave()
}
}
extension ViewController: @preconcurrency JitsiMeetViewDelegate {
// MARK: - Private Helper Methods
private func onJitsiMeetViewDelegateEvent(_ name: String, withData data: [AnyHashable: Any]?) {
NSLog("[%@:%d] JitsiMeetViewDelegate %@ %@", #file, #line, name, data ?? [:])
#if DEBUG
assert(Thread.isMainThread, "JitsiMeetViewDelegate \(name) method invoked on a non-main thread")
#endif
}
// MARK: - JitsiMeetViewDelegate
func conferenceJoined(_ data: [AnyHashable: Any]) {
onJitsiMeetViewDelegateEvent("CONFERENCE_JOINED", withData: data)
// Register a NSUserActivity for this conference so it can be invoked as a Siri shortcut.
// Must match the one defined in Info.plist
let userActivity = NSUserActivity(activityType: "org.jitsi.JitsiMeet.ios.conference")
if let urlStr = data["url"] as? String,
let url = URL(string: urlStr),
let conference = url.pathComponents.last {
userActivity.title = "Join \(conference)"
userActivity.suggestedInvocationPhrase = "Join my Jitsi meeting"
userActivity.userInfo = ["url": urlStr]
userActivity.isEligibleForSearch = true
userActivity.isEligibleForPrediction = true
userActivity.persistentIdentifier = urlStr
// Subtitle
let attributes = CSSearchableItemAttributeSet(contentType: UTType.item)
attributes.contentDescription = urlStr
userActivity.contentAttributeSet = attributes
self.userActivity = userActivity
userActivity.becomeCurrent()
}
}
func ready(toClose data: [AnyHashable: Any]) {
onJitsiMeetViewDelegateEvent("READY_TO_CLOSE", withData: data)
}
}

28
ios/app/src/main.m Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* 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 <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(
argc, argv,
nil,
NSStringFromClass([AppDelegate class]));
}
}

View File

@@ -1,15 +0,0 @@
diff --git a/SocketRocket/SRSecurityPolicy.m b/SocketRocket/SRSecurityPolicy.m
index 3759d26e..271477e8 100644
--- a/SocketRocket/SRSecurityPolicy.m
+++ b/SocketRocket/SRSecurityPolicy.m
@@ -56,8 +56,8 @@ - (instancetype)init
- (void)updateSecurityOptionsInStream:(NSStream *)stream
{
- // Enforce TLS 1.2
- [stream setProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
+ // Enforce TLS >= 1.2
+ [stream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
// Validate certificate chain for this stream if enabled.
NSDictionary<NSString *, id> *sslOptions = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain : @(self.certificateChainValidationEnabled) };

View File

@@ -145,6 +145,7 @@
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ../PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
86389F55993FAAF6AEB3FA3E /* Pods-JitsiMeetSDKLite.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.release.xcconfig"; sourceTree = "<group>"; };
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
8F48C340DE0D91D1012976C5 /* Pods-JitsiMeetSDKLite.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDKLite.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDKLite/Pods-JitsiMeetSDKLite.debug.xcconfig"; sourceTree = "<group>"; };
@@ -230,6 +231,7 @@
0BD906E61EC0C00300C8C18E /* Products */,
0BCA49681EC4BBE500B793EE /* Resources */,
0BD906E71EC0C00300C8C18E /* src */,
6132EF172BDFF13200BBE14D /* PrivacyInfo.xcprivacy */,
);
sourceTree = "<group>";
};
@@ -869,7 +871,6 @@
baseConfigurationReference = 09A78016288AF50ACD28A10D /* Pods-JitsiMeetSDK.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -893,6 +894,8 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_OBJC_INTERFACE_HEADER_NAME = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -904,7 +907,6 @@
baseConfigurationReference = 891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -928,6 +930,8 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_OBJC_INTERFACE_HEADER_NAME = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -938,7 +942,6 @@
baseConfigurationReference = 8F48C340DE0D91D1012976C5 /* Pods-JitsiMeetSDKLite.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -967,6 +970,8 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_OBJC_INTERFACE_HEADER_NAME = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -978,7 +983,6 @@
baseConfigurationReference = 86389F55993FAAF6AEB3FA3E /* Pods-JitsiMeetSDKLite.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -1007,6 +1011,8 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_OBJC_INTERFACE_HEADER_NAME = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};

View File

@@ -18,6 +18,11 @@
static NSString * const sendEventNotificationName = @"org.jitsi.meet.SendEvent";
typedef NS_ENUM(NSInteger, RecordingMode) {
RecordingModeFile,
RecordingModeStream
};
@interface ExternalAPI : RCTEventEmitter<RCTBridgeModule>
- (void)sendHangUp;
@@ -33,9 +38,7 @@ static NSString * const sendEventNotificationName = @"org.jitsi.meet.SendEvent";
- (void)toggleCamera;
- (void)showNotification:(NSString*)appearance :(NSString*)description :(NSString*)timeout :(NSString*)title :(NSString*)uid;
- (void)hideNotification:(NSString*)uid;
- (void)startRecording:(NSString*)mode :(NSString*)dropboxToken :(BOOL)shouldShare :(NSString*)rtmpStreamKey :(NSString*)rtmpBroadcastID :(NSString*)youtubeStreamKey :(NSString*)youtubeBroadcastID :(NSDictionary*)extraMetadata :(BOOL)transcription;
- (void)stopRecording:(NSString*)mode :(BOOL)transcription;
- (void)overwriteConfig:(NSDictionary*)config;
- (void)sendCameraFacingModeMessage:(NSString*)to :(NSString*)facingMode;
- (void)startRecording:(RecordingMode)mode :(NSString*)dropboxToken :(BOOL)shouldShare :(NSString*)rtmpStreamKey :(NSString*)rtmpBroadcastID :(NSString*)youtubeStreamKey :(NSString*)youtubeBroadcastID :(NSDictionary*)extraMetadata :(BOOL)transcription;
- (void)stopRecording:(RecordingMode)mode :(BOOL)transcription;
@end

View File

@@ -32,15 +32,13 @@ static NSString * const showNotificationAction = @"org.jitsi.meet.SHOW_NOTIFICAT
static NSString * const hideNotificationAction = @"org.jitsi.meet.HIDE_NOTIFICATION";
static NSString * const startRecordingAction = @"org.jitsi.meet.START_RECORDING";
static NSString * const stopRecordingAction = @"org.jitsi.meet.STOP_RECORDING";
static NSString * const overwriteConfigAction = @"org.jitsi.meet.OVERWRITE_CONFIG";
static NSString * const sendCameraFacingModeMessageAction = @"org.jitsi.meet.SEND_CAMERA_FACING_MODE_MESSAGE";
@implementation ExternalAPI
static NSMapTable<NSString*, void (^)(NSArray* participantsInfo)> *participantInfoCompletionHandlers;
__attribute__((constructor))
static void initializeViewsMap(void) {
static void initializeViewsMap() {
participantInfoCompletionHandlers = [NSMapTable strongToStrongObjectsMapTable];
}
@@ -62,9 +60,7 @@ RCT_EXPORT_MODULE();
@"SHOW_NOTIFICATION": showNotificationAction,
@"HIDE_NOTIFICATION": hideNotificationAction,
@"START_RECORDING": startRecordingAction,
@"STOP_RECORDING": stopRecordingAction,
@"OVERWRITE_CONFIG": overwriteConfigAction,
@"SEND_CAMERA_FACING_MODE_MESSAGE": sendCameraFacingModeMessageAction
@"STOP_RECORDING": stopRecordingAction
};
};
@@ -94,9 +90,7 @@ RCT_EXPORT_MODULE();
showNotificationAction,
hideNotificationAction,
startRecordingAction,
stopRecordingAction,
overwriteConfigAction,
sendCameraFacingModeMessageAction
stopRecordingAction
];
}
@@ -216,9 +210,21 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
[self sendEventWithName:hideNotificationAction body:data];
}
- (void)startRecording:(NSString*)mode :(NSString*)dropboxToken :(BOOL)shouldShare :(NSString*)rtmpStreamKey :(NSString*)rtmpBroadcastID :(NSString*)youtubeStreamKey :(NSString*)youtubeBroadcastID :(NSDictionary*)extraMetadata :(BOOL)transcription {
static inline NSString *RecordingModeToString(RecordingMode mode) {
switch (mode) {
case RecordingModeFile:
return @"file";
case RecordingModeStream:
return @"stream";
default:
return nil;
}
}
- (void)startRecording:(RecordingMode)mode :(NSString*)dropboxToken :(BOOL)shouldShare :(NSString*)rtmpStreamKey :(NSString*)rtmpBroadcastID :(NSString*)youtubeStreamKey :(NSString*)youtubeBroadcastID :(NSDictionary*)extraMetadata :(BOOL)transcription {
NSString *modeString = RecordingModeToString(mode);
NSDictionary *data = @{
@"mode": mode,
@"mode": modeString,
@"dropboxToken": dropboxToken,
@"shouldShare": @(shouldShare),
@"rtmpStreamKey": rtmpStreamKey,
@@ -232,29 +238,13 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
[self sendEventWithName:startRecordingAction body:data];
}
- (void)stopRecording:(NSString*)mode :(BOOL)transcription {
- (void)stopRecording:(RecordingMode)mode :(BOOL)transcription {
NSString *modeString = RecordingModeToString(mode);
NSDictionary *data = @{
@"mode": mode,
@"mode": modeString,
@"transcription": @(transcription)
};
[self sendEventWithName:stopRecordingAction body:data];
}
- (void)overwriteConfig:(NSDictionary*)config {
NSDictionary *data = @{
@"config": config
};
[self sendEventWithName:overwriteConfigAction body:data];
}
- (void)sendCameraFacingModeMessage:(NSString*)to :(NSString*)facingMode {
NSDictionary *data = @{
@"to": to,
@"facingMode": facingMode
};
[self sendEventWithName:sendCameraFacingModeMessageAction body:data];
}
@end

View File

@@ -24,11 +24,15 @@
}
+ (void)activateWithAudioSession:(AVAudioSession *)session {
[self.rtcAudioSession audioSessionDidActivate:session];
if (!self.rtcAudioSession.useManualAudio) {
[self.rtcAudioSession audioSessionDidActivate:session];
}
}
+ (void)deactivateWithAudioSession:(AVAudioSession *)session {
[self.rtcAudioSession audioSessionDidDeactivate:session];
if (!self.rtcAudioSession.useManualAudio) {
[self.rtcAudioSession audioSessionDidDeactivate:session];
}
}
@end

View File

@@ -82,7 +82,7 @@ typedef NS_ENUM(NSInteger, WebRTCLoggingSeverity) {
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *_Nonnull)options;
- (UIInterfaceOrientationMask)application:(UIApplication *_Nonnull)application
supportedInterfaceOrientationsForWindow:(UIWindow *_Nullable)window;
supportedInterfaceOrientationsForWindow:(UIWindow *_Nonnull)window;
#pragma mark - Utility methods
@@ -100,9 +100,6 @@ typedef NS_ENUM(NSInteger, WebRTCLoggingSeverity) {
- (BOOL)isCrashReportingDisabled;
/**
* Shows the splash screen.
*/
- (void)showSplashScreen;
- (void)showSplashScreen:(UIView * _Nonnull) rootView;
@end

View File

@@ -23,6 +23,7 @@
#import "JitsiMeetView+Private.h"
#import "RCTBridgeWrapper.h"
#import "ReactUtils.h"
#import "RNSplashScreen.h"
#import "ScheenshareEventEmiter.h"
#import <react-native-webrtc/WebRTCModuleOptions.h>
@@ -220,17 +221,8 @@
return nil;
}
- (void)showSplashScreen {
Class splashClass = NSClassFromString(@"SplashView");
if (splashClass && [splashClass respondsToSelector:@selector(sharedInstance)]) {
id splashInstance = [splashClass performSelector:@selector(sharedInstance)];
if (splashInstance && [splashInstance respondsToSelector:@selector(showSplash)]) {
[splashInstance performSelector:@selector(showSplash)];
NSLog(@"✅ Splash Screen Shown Successfully");
}
} else {
NSLog(@"⚠️ SplashView module not found");
}
- (void)showSplashScreen:(UIView*)rootView {
[RNSplashScreen showSplash:@"LaunchScreen" inRootView:rootView];
}
#pragma mark - Property getter / setters

View File

@@ -21,10 +21,7 @@
#import "JitsiMeetConferenceOptions.h"
#import "JitsiMeetViewDelegate.h"
typedef NS_ENUM(NSInteger, RecordingMode) {
RecordingModeFile,
RecordingModeStream
};
typedef NS_ENUM(NSInteger, RecordingMode);
@interface JitsiMeetView : UIView
@@ -54,8 +51,7 @@ typedef NS_ENUM(NSInteger, RecordingMode) {
- (void)toggleCamera;
- (void)showNotification:(NSString * _Nonnull)appearance :(NSString * _Nullable)description :(NSString * _Nullable)timeout :(NSString * _Nullable)title :(NSString * _Nullable)uid;
- (void)hideNotification:(NSString * _Nullable)uid;
- (void)startRecording:(RecordingMode)mode :(NSString * _Nullable)dropboxToken :(BOOL)shouldShare :(NSString * _Nullable)rtmpStreamKey :(NSString * _Nullable)rtmpBroadcastID :(NSString * _Nullable)youtubeStreamKey :(NSString * _Nullable)youtubeBroadcastID :(NSDictionary * _Nullable)extraMetadata :(BOOL)transcription;
- (void)startRecording:(RecordingMode)mode :(NSString * _Nullable)dropboxToken :(BOOL)shouldShare :(NSString * _Nullable)rtmpStreamKey :(NSString * _Nullable)rtmpBroadcastID :(NSString * _Nullable)youtubeStreamKey :(NSString * _Nullable)youtubeBroadcastID :(NSString * _Nullable)extraMetadata :(BOOL)transcription;
- (void)stopRecording:(RecordingMode)mode :(BOOL)transcription;
- (void)overwriteConfig:(NSDictionary * _Nonnull)config;
- (void)sendCameraFacingModeMessage:(NSString * _Nonnull)to :(NSString * _Nullable)facingMode;
@end

View File

@@ -17,8 +17,6 @@
#include <mach/mach_time.h>
#import <UIKit/UIKit.h>
#import "ExternalAPI.h"
#import "JitsiMeet+Private.h"
#import "JitsiMeetConferenceOptions+Private.h"
@@ -27,43 +25,11 @@
#import "RNRootView.h"
#pragma mark UIColor helpers
@interface UIColor (Hex)
+ (UIColor *)colorWithHex:(uint32_t)hex;
+ (UIColor *)colorWithHex:(uint32_t)hex alpha:(CGFloat)alpha;
@end
@implementation UIColor (Hex)
+ (UIColor *)colorWithHex:(uint32_t)hex {
return [self colorWithHex:hex alpha:1.0];
}
+ (UIColor *)colorWithHex:(uint32_t)hex alpha:(CGFloat)alpha {
CGFloat red = ((hex >> 16) & 0xFF) / 255.0;
CGFloat green = ((hex >> 8) & 0xFF) / 255.0;
CGFloat blue = (hex & 0xFF) / 255.0;
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}
@end
#pragma mark UIColor helpers end
/**
* Backwards compatibility: turn the boolean prop into a feature flag.
*/
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
/**
* Forward declarations.
*/
static NSString *recordingModeToString(RecordingMode mode);
@implementation JitsiMeetView {
/**
@@ -99,8 +65,11 @@ static NSString *recordingModeToString(RecordingMode mode);
* - registers necessary observers
*/
- (void)doInitialize {
// Set a background color which matches the one used in JS.
self.backgroundColor = [UIColor colorWithHex:0x040404 alpha:1];
// Set a background color which is in accord with the JavaScript and Android
// parts of the application and causes less perceived visual flicker than
// the default background color.
self.backgroundColor
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
[self registerObservers];
}
@@ -184,25 +153,15 @@ static NSString *recordingModeToString(RecordingMode mode);
[externalAPI hideNotification:uid];
}
- (void)startRecording:(RecordingMode)mode :(NSString * _Nullable)dropboxToken :(BOOL)shouldShare :(NSString * _Nullable)rtmpStreamKey :(NSString * _Nullable)rtmpBroadcastID :(NSString * _Nullable)youtubeStreamKey :(NSString * _Nullable)youtubeBroadcastID :(NSDictionary * _Nullable)extraMetadata :(BOOL)transcription {
- (void)startRecording:(RecordingMode)mode :(NSString *)dropboxToken :(BOOL)shouldShare :(NSString *)rtmpStreamKey :(NSString *)rtmpBroadcastID :(NSString *)youtubeStreamKey :(NSString *)youtubeBroadcastID :(NSString *)extraMetadata :(BOOL)transcription {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI startRecording:recordingModeToString(mode) :dropboxToken :shouldShare :rtmpStreamKey :rtmpBroadcastID :youtubeStreamKey :youtubeBroadcastID :extraMetadata :transcription];
[externalAPI startRecording:mode :dropboxToken :shouldShare :rtmpStreamKey :rtmpBroadcastID :youtubeStreamKey :youtubeBroadcastID :extraMetadata :transcription];
}
- (void)stopRecording:(RecordingMode)mode :(BOOL)transcription {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI stopRecording:recordingModeToString(mode) :transcription];
}
- (void)overwriteConfig:(NSDictionary * _Nonnull)config {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI overwriteConfig:config];
}
- (void)sendCameraFacingModeMessage:(NSString * _Nonnull)to :(NSString * _Nullable)facingMode {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendCameraFacingModeMessage:to :facingMode];
}
[externalAPI stopRecording:mode :transcription];
}
#pragma mark Private methods
@@ -298,14 +257,3 @@ static NSString *recordingModeToString(RecordingMode mode);
}
@end
static NSString *recordingModeToString(RecordingMode mode) {
switch (mode) {
case RecordingModeFile:
return @"file";
case RecordingModeStream:
return @"stream";
default:
return nil;
}
}

View File

@@ -130,18 +130,4 @@
*/
- (void)customButtonPressed:(NSDictionary *)data;
/**
* Called when the unique identifier for conference has been set.
*
* The `data` dictionary contains a `sessionId` key.
*/
- (void)conferenceUniqueIdSet:(NSDictionary *)data;
/**
* Called when the recording status has changed.
*
* The `data` dictionary contains a `sessionData` key.
*/
- (void)recordingStatusChanged:(NSDictionary *)data;
@end

View File

@@ -39,7 +39,6 @@
"mr": "मराठी",
"nb": "Norsk bokmål",
"nl": "Nederlands",
"no": "Norsk",
"oc": "Occitan",
"pl": "Polski",
"pt": "Português",

View File

@@ -835,6 +835,7 @@
"or": "أو",
"premeeting": "ما قبل المُلتقى",
"screenSharingError": "خطأ في مشاركة الشاشة:",
"showScreen": "تفعيل واجهة ما قبل المُلتقى",
"startWithPhone": "البدء مع جهاز الصوت من الجوال",
"videoOnlyError": "خطأ في الفيديو:",
"videoTrackError": "لم نتمكن من إنشاء ملف الفيديو",
@@ -943,7 +944,6 @@
"title": "خيارات الأمان"
},
"settings": {
"audio": "الصوت",
"buttonLabel": "إعدادات",
"calendar": {
"about": "تُستعمَل الرزنامة {{appName}} المدمجة للوصل بأمان إلى رزنامتك، لذا بالإمكان معرفة الأحداث القادمة.",
@@ -967,7 +967,6 @@
"more": "المزيد",
"name": "الاسم",
"noDevice": "لا يوجد",
"notifications": "الإشعارات",
"participantJoined": "انضم مشارك",
"participantKnocking": "دخل المشارك في الردهة",
"participantLeft": "غادر المشارك",
@@ -978,15 +977,13 @@
"selectCamera": "الكاميرا",
"selectMic": "المايكروفون",
"selfView": "عرض ذاتي",
"shortcuts": "اختصارات لوحة المفاتيح",
"sounds": "اصوات",
"speakers": "المذياع (مكبر الصوت)",
"startAudioMuted": "بدء الجميع مكتومي الصوت",
"startReactionsMuted": "كتم رد فعل الصوت للجميع",
"startVideoMuted": "بدء الجميع دون فيديو",
"talkWhileMuted": "تحدث أثناء كتم الصوت",
"title": "الإعدادات",
"video": "الفيديو"
"title": "الإعدادات"
},
"settingsView": {
"advanced": "إعدادات متقدمة",

View File

@@ -842,6 +842,7 @@
"or": "o",
"premeeting": "Prereunió",
"screenSharingError": "Error en compartir la pantalla:",
"showScreen": "Activa la pantalla de prereunió",
"startWithPhone": "Comença amb àudio de telèfon",
"videoOnlyError": "Error del vídeo:",
"videoTrackError": "No s'ha pogut crear la pista de vídeo.",
@@ -915,7 +916,7 @@
"localRecordingStartWarningTitle": "Atura l'enregistrament per a desar-lo",
"localRecordingVideoStop": "Aturar el vídeo també aturarà l'enregistrament local. Segur que voleu continuar?",
"localRecordingVideoWarning": "Per a enregistrar el vostre vídeo, cal que ho feu en començar l'enregistrament",
"localRecordingWarning": "Assegureu-vos de triar la pestanya actual per a poder usar el vídeo i àudios correctes.",
"localRecordingWarning": "Assegureu-vos de triar la pestanya actual per a poder usar el vídeo i àudios correctes. L'enregistrament actualment està limitat a 1 GB. Això són, aproximadament, 100 minuts.",
"loggedIn": "Sessió iniciada com a {{userName}}",
"noStreams": "No s'ha detectat flux d'àudio ni vídeo.",
"off": "S'ha aturat l'enregistrament",

View File

@@ -976,6 +976,7 @@
"proceedAnyway": "Přesto pokračujte",
"recordingWarning": "Ostatní účastníci mohou tento hovor nahrávat",
"screenSharingError": "Chyba sdílení obrazovky:",
"showScreen": "Zapnout obrazovku před setkáním",
"startWithPhone": "Začít se zvukem přes telefon",
"unsafeRoomConsent": "Chápu rizika, chci se připojit k setkání",
"videoOnlyError": "Chyba videa:",
@@ -1046,7 +1047,7 @@
"localRecordingStartWarningTitle": "Zastavte záznam, abyste jej uložili",
"localRecordingVideoStop": "Zastavením videa se zastaví také místní nahrávání. Opravdu chcete pokračovat?",
"localRecordingVideoWarning": "Chcete-li nahrát video, musíte jej mít zapnutý při zahájení nahrávání",
"localRecordingWarning": "Ujistěte se, že jste vybrali aktuální kartu, abyste mohli použít správné video a zvuk.",
"localRecordingWarning": "Ujistěte se, že jste vybrali aktuální kartu, abyste mohli použít správné video a zvuk. Záznam je aktuálně omezen na 1GB, což je kolem 100 minut.",
"loggedIn": "Přihlášen/a jako {{userName}}",
"noMicPermission": "Kanál mikrofonu nelze vytvořit. Udělte prosím povolení k použití mikrofonu.",
"noStreams": "Nebyl zjištěn žádný audio nebo video stream.",
@@ -1110,7 +1111,7 @@
"incomingMessage": "Příchozí zpráva",
"language": "Jazyk",
"loggedIn": "Přihlášen/a jako {{name}}",
"maxStageParticipants": "Maximální počet účastníků, které lze připnout na hlavní pódium",
"maxStageParticipants": "Maximální počet účastníků, které lze připnout na hlavní pódium (EXPERIMENTÁLNÍ)",
"microphones": "Mikrofony",
"moderator": "Moderátor",
"moderatorOptions": "Možnosti moderátora",

View File

@@ -70,14 +70,14 @@
"breakoutList": "Breakout-Liste",
"buttonLabel": "Breakout-Räume",
"defaultName": "Breakout-Raum #{{index}}",
"hideParticipantList": "Personenliste ausblenden",
"hideParticipantList": "Teilnehmerliste ausblenden",
"mainRoom": "Hauptraum",
"notifications": {
"joined": "Breakout-Raum \"{{name}}\" betreten",
"joinedMainRoom": "Hauptraum betreten",
"joinedTitle": "Breakout-Räume"
},
"showParticipantList": "Personenliste anzeigen",
"showParticipantList": "Teilnehmerliste anzeigen",
"title": "Breakout-Räume"
},
"calendarSync": {
@@ -89,7 +89,7 @@
"notSignedIn": "Ein Fehler ist während der Authentifizierung zur Anzeige von Kalenderterminen aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, sich erneut anzumelden."
},
"join": "Teilnehmen",
"joinTooltip": "An Konferenz teilnehmen",
"joinTooltip": "Am Konferenz teilnehmen",
"nextMeeting": "Nächste Konferenz",
"noEvents": "Es gibt keine bevorstehenden Termine.",
"ongoingMeeting": "Laufende Konferenz",
@@ -122,9 +122,7 @@
"nickname": {
"popover": "Wähle einen Alias",
"title": "Geben Sie einen Alias zum Chatten ein",
"titleWithCC": "Geben Sie einen Alias zum Chatten und für Untertitel ein",
"titleWithPolls": "Geben Sie einen Alias zum Chatten und für Umfragen ein",
"titleWithPollsAndCC": "Geben Sie einen Alias zum Chatten, für Umfragen und Untertitel ein"
"titleWithPolls": "Geben Sie einen Alias zum Chatten ein"
},
"noMessagesMessage": "Es gibt noch keine Nachricht in dieser Konferenz. Starten Sie hier eine Unterhaltung!",
"privateNotice": "Private Nachricht an {{recipient}}",
@@ -133,13 +131,10 @@
"systemDisplayName": "System",
"tabs": {
"chat": "Chatten",
"closedCaptions": "Untertitel",
"polls": "Umfragen"
},
"title": "Chatten",
"titleWithCC": "Chatten und Untertitel",
"titleWithPolls": "Chatten und Umfragen",
"titleWithPollsAndCC": "Chatten, Umfragen und Untertitel",
"you": "Sie"
},
"chromeExtensionBanner": {
@@ -149,10 +144,6 @@
"dontShowAgain": "Hinweis nicht mehr anzeigen",
"installExtensionText": "Installieren Sie die Erweiterung für die Integration von Google Calendar und Office 365"
},
"closedCaptionsTab": {
"emptyState": "Die Untertitel sind verfügbar, sobald sie von der Moderation gestartet wurden",
"startClosedCaptionsButton": "Untertitel starten"
},
"connectingOverlay": {
"joiningRoom": "Eine Verbindung zu Ihrer Konferenz wird hergestellt…"
},
@@ -272,8 +263,6 @@
"Remove": "Entfernen",
"Share": "Teilen",
"Submit": "OK",
"Understand": "Verstanden, Stummschaltung beibehalten",
"UnderstandAndUnmute": "Verstanden, bitte Stummschaltung aufheben",
"WaitForHostMsg": "Die Konferenz wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
"WaitForHostNoAuthMsg": "Die Konferenz wurde noch nicht gestartet. Bitte warten Sie, bis die Konferenz gestartet wird.",
"WaitingForHostButton": "Auf Moderation warten",
@@ -310,7 +299,6 @@
"conferenceReloadMsg": "Wir versuchen das zu beheben. Verbinde in {{seconds}} Sekunden …",
"conferenceReloadTitle": "Leider ist etwas schiefgegangen.",
"confirm": "Bestätigen",
"confirmBack": "Zurück",
"confirmNo": "Nein",
"confirmYes": "Ja",
"connectError": "Oh! Es hat etwas nicht geklappt und der Konferenz konnte nicht beigetreten werden.",
@@ -348,7 +336,6 @@
"kickParticipantTitle": "Person entfernen?",
"kickSystemTitle": "Autsch! Sie wurden aus der Konferenz geworfen",
"kickTitle": "Autsch! {{participantDisplayName}} hat Sie aus der Konferenz geworfen",
"learnMore": "Mehr erfahren",
"linkMeeting": "Konferenz verlinken",
"linkMeetingTitle": "Konferenz mit Salesforce verlinken",
"liveStreaming": "Livestreaming",
@@ -376,18 +363,18 @@
"muteEveryoneDialogModerationOn": "Die Anwesenden können eine Anfrage zum Sprechen jederzeit senden.",
"muteEveryoneElseDialog": "Einmal stummgeschaltet, können Sie deren Stummschaltung nicht mehr beenden, aber sie können ihre Stummschaltung jederzeit selbst beenden.",
"muteEveryoneElseTitle": "Alle außer {{whom}} stummschalten?",
"muteEveryoneElsesVideoDialog": "Sobald die Kamera für alle anderen Personen deaktiviert ist, können Sie diese nicht wieder für alle einschalten, die anderen Personen können ihre Kamera aber jederzeit wieder einschalten.",
"muteEveryoneElsesVideoDialog": "Sobald die Kamera deaktiviert ist, können Sie sie nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"muteEveryoneElsesVideoTitle": "Die Kamera von allen außer {{whom}} ausschalten?",
"muteEveryoneSelf": "sich selbst",
"muteEveryoneStartMuted": "Alle beginnen von jetzt an stummgeschaltet",
"muteEveryoneTitle": "Alle stummschalten?",
"muteEveryonesVideoDialog": "Sind Sie sicher, dass Sie die Kamera von allen Personen deaktivieren möchten? Sie können dies nicht wieder rückgängig machen, jede Personen kann ihre Kamera aber jederzeit wieder einschalten.",
"muteEveryonesVideoDialog": "Sind Sie sicher, dass Sie die Kamera von allen Teilnehmern deaktivieren möchten? Sie können sie nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"muteEveryonesVideoDialogModerationOn": "Die Anwesenden können jederzeit eine Anfrage senden, um ihre Kamera einzuschalten.",
"muteEveryonesVideoDialogOk": "deaktivieren",
"muteEveryonesVideoTitle": "Die Kamera von allen anderen ausschalten?",
"muteParticipantBody": "Sie können die Stummschaltung anderer Personen nicht aufheben, aber eine Person kann ihre eigene Stummschaltung jederzeit beenden.",
"muteParticipantButton": "Stummschalten",
"muteParticipantsVideoBody": "Sie können die Kamera nicht wieder einschalten, die Person kann ihre Kamera aber jederzeit wieder einschalten.",
"muteParticipantsVideoBody": "Sie können die Kamera nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"muteParticipantsVideoBodyModerationOn": "Sie können die Kamera nicht wieder aktivieren und die Person selbst auch nicht.",
"muteParticipantsVideoButton": "Kamera ausschalten",
"muteParticipantsVideoDialog": "Wollen Sie die Kamera dieser Person wirklich deaktivieren? Sie können die Kamera nicht wieder aktivieren, die Person kann dies aber jederzeit selbst tun.",
@@ -406,10 +393,6 @@
"recentlyUsedObjects": "Ihre zuletzt verwendeten Objekte",
"recording": "Aufnahme",
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Während eines Livestreams nicht möglich",
"recordingInProgressDescription": "Diese Konferenz wird aufgezeichnet und von KI analysiert {{learnMore}}. Ihr Ton und Video ist deaktiviert, wenn Sie es aktivieren, stimmen Sie der Aufzeichnung zu.",
"recordingInProgressDescriptionFirstHalf": "Diese Konferenz wird aufgezeichnet und von KI analysiert",
"recordingInProgressDescriptionSecondHalf": ". Ihr Ton und Video ist deaktiviert, wenn Sie es aktivieren, stimmen Sie der Aufzeichnung zu.",
"recordingInProgressTitle": "Aufnahme läuft",
"rejoinNow": "Jetzt erneut beitreten",
"remoteControlAllowedMessage": "{{user}} hat die Anfrage zur Fernsteuerung angenommen!",
"remoteControlDeniedMessage": "{{user}} hat die Anfrage zur Fernsteuerung verweigert!",
@@ -579,7 +562,7 @@
"inviteTextiOSPhone": "Nutzen Sie folgende Nummer um via Telefon teilzunehmen: {{number}},,{{conferenceID}}#. Wenn Sie nach einer anderen Einwahlnummer suchen, finden Sie die vollständige Liste hier: {{didUrl}}.",
"inviteURLFirstPartGeneral": "Sie wurden zur Teilnahme an einer Konferenz eingeladen.",
"inviteURLFirstPartPersonal": "{{name}} lädt Sie zu einer Konferenz ein.\n",
"inviteURLSecondPart": "\nAn Konferenz teilnehmen:\n{{url}}\n",
"inviteURLSecondPart": "\nAm Konferenz teilnehmen:\n{{url}}\n",
"label": "Einwahlinformationen",
"liveStreamURL": "Livestream:",
"moreNumbers": "Weitere Telefonnummern",
@@ -659,7 +642,6 @@
"on": "Livestream",
"onBy": "{{name}} startete den Livestream",
"pending": "Livestream wird gestartet …",
"policyError": "Sie haben den Livestream zu schnell gestartet. Bitte versuchen Sie es später noch einmal!",
"serviceName": "Livestreaming-Dienst",
"sessionAlreadyActive": "Diese Konferenz wird bereits als Livestream übertragen.",
"signIn": "Mit Google anmelden",
@@ -760,14 +742,13 @@
"connectedOneMember": "{{name}} nimmt an der Konferenz teil",
"connectedThreePlusMembers": "{{name}} und {{count}} andere Personen nehmen an der Konferenz teil",
"connectedTwoMembers": "{{first}} und {{second}} nehmen an der Konferenz teil",
"connectionFailed": "Verbindung fehlgeschlagen. Bitte versuchen Sie es später noch einmal!",
"connectionFailed": "Verbindung fehlgeschlagen. Bitte versuchen Sie es später noch einmal.",
"dataChannelClosed": "Schlechte Videoqualität",
"dataChannelClosedDescription": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher ist die Videoqulität auf die schlechteste Stufe limitiert.",
"dataChannelClosedDescriptionWithAudio": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher können Video- und Tonprobleme auftreten.",
"dataChannelClosedWithAudio": "Ton- und Videoqualität können beeinträchtigt sein",
"disabledIframe": "Die Einbettung ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
"disabledIframeSecondaryNative": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
"disabledIframeSecondaryWeb": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
"disabledIframeSecondary": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
"disconnected": "getrennt",
"displayNotifications": "Benachrichtigungen anzeigen für",
"dontRemindMe": "Nicht erinnern",
@@ -776,9 +757,9 @@
"gifsMenu": "GIPHY",
"groupTitle": "Benachrichtigungen",
"hostAskedUnmute": "Die Moderation bittet Sie, das Mikrofon zu aktivieren",
"invalidTenant": "Ungültiger Mandant",
"invalidTenantHyphenDescription": "Der gewählte Mandantenname ist ungültig (beginnt oder endet mit '-').",
"invalidTenantLengthDescription": "Der gewählte Mandantenname ist zu lang.",
"invalidTenant": "Ungültiger Tenant",
"invalidTenantHyphenDescription": "Der von Ihnen genutzte Tenantname ist unfültig (beginnt oder endet mit '-').",
"invalidTenantLengthDescription": "Der von Ihnen genutzte Tenantname ist zu lang.",
"invitedOneMember": "{{name}} wurde eingeladen",
"invitedThreePlusMembers": "{{name}} und {{count}} andere wurden eingeladen",
"invitedTwoMembers": "{{first}} und {{second}} wurden eingeladen",
@@ -895,7 +876,6 @@
"waitingLobby": "In der Lobby ({{count}})"
},
"search": "Suche Anwesende",
"searchDescription": "Tippen Sie um die Anwesendenliste zu filtern",
"title": "Anwesende"
},
"passwordDigitsOnly": "Bis zu {{number}} Ziffern",
@@ -998,6 +978,7 @@
"proceedAnyway": "Trotzdem fortsetzen",
"recordingWarning": "Diese Konferenz wird möglicherweise von anderen Personen aufgezeichnet",
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
"showScreen": "Konferenzvorschau aktivieren",
"startWithPhone": "Mit Telefonaudio starten",
"unsafeRoomConsent": "Ich verstehe das Risiko und möchte der Konferenz beitreten",
"videoOnlyError": "Videofehler:",
@@ -1068,7 +1049,7 @@
"localRecordingStartWarningTitle": "Aufzeichnung zum Speichern beenden",
"localRecordingVideoStop": "Wenn Sie ihre Kamera abschalten wird auch die Aufnahme beendet. Sind Sie sicher, dass Sie fortfahren möchten?",
"localRecordingVideoWarning": "Um Ihr eigenes Kamerabild aufzuzeichnen, müssen Sie Ihre Kamera beim Start der Aufnahme einschalten",
"localRecordingWarning": "Bitte prüfen Sie, dass das aktuelle Tab auswählen, um Bild und Ton aufzuzeichnen.",
"localRecordingWarning": "Bitte prüfen Sie, dass das aktuelle Tab auswählen, um Bild und Ton aufzuzeichnen. Die Länge der Aufzeichnung ist aktuell auf 1GB beschränkt, was ungefähr 100 Minuten entspricht.",
"loggedIn": "Als {{userName}} angemeldet",
"noMicPermission": "Zugriff auf Mikrofon fehlgeschlagen. Bitte erlauben Sie den Zugriff auf das Mikrofon.",
"noStreams": "Kein Ton oder Video erkannt.",
@@ -1078,7 +1059,7 @@
"onBy": "{{name}} startete die Aufnahme",
"onlyRecordSelf": "Nur eigenes Kamerabild und Ton aufzeichnen",
"pending": "Aufzeichnung der Konferenz wird vorbereitet…",
"policyError": "Sie haben die Aufzeichnung zu schnell gestartet. Bitte versuchen Sie es später noch einmal.",
"policyError": "Sie haben die Aufzeichnung zu früh gestartet. Bitte versuchen Sie es später noch einmal.",
"recordAudioAndVideo": "Kamera und Ton aufzeichnen",
"recordTranscription": "Transkription aufzeichnen",
"saveLocalRecording": "Aufzeichnung lokal abspeichern",
@@ -1101,10 +1082,10 @@
"pullToRefresh": "Ziehen, um zu aktualisieren"
},
"security": {
"about": "Sie können Ihre Konferenz mit einem Passwort sichern. Personen müssen dieses eingeben, bevor sie an der Sitzung teilnehmen dürfen.",
"about": "Sie können Ihre Konferenz mit einem Passwort sichern. Teilnehmer müssen dieses eingeben, bevor sie an der Sitzung teilnehmen dürfen.",
"aboutReadOnly": "Mit Moderationsrechten kann die Konferenz mit einem Passwort gesichert werden. Personen müssen dieses eingeben, bevor sie an der Sitzung teilnehmen dürfen.",
"insecureRoomNameWarningNative": "Der Raumname ist unsicher. Unerwünschte Personen könnten Ihrer Konferenz beitreten. {{recommendAction}} Lernen Sie mehr über die Absicherung Ihrer Konferenz ",
"insecureRoomNameWarningWeb": "Der Raumname ist unsicher. Unerwünschte Personen könnten Ihrer Konferenz beitreten {{recommendAction}} Lernen Sie <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">hier</a> mehr über die Absicherung Ihrer Konferenz.",
"insecureRoomNameWarningNative": "Der Raumname ist unsicher. Unerwünschte Teilnehmer könnten Ihrer Konferenz beitreten. {{recommendAction}} Lernen Sie mehr über die Absicherung Ihrer Konferenz ",
"insecureRoomNameWarningWeb": "Der Raumname ist unsicher. Unerwünschte Teilnehmer könnten Ihrer Konferenz beitreten {{recommendAction}} Lernen Sie <a href=\"{{securityUrl}}\" rel=\"security\" target=\"_blank\">hier</a> mehr über die Absicherung Ihrer Konferenz.",
"title": "Sicherheitsoptionen",
"unsafeRoomActions": {
"meeting": "Erwägen Sie die Absicherung Ihrer Konferenz über den Sicherheits-Button.",
@@ -1122,7 +1103,6 @@
"signedIn": "Momentan wird auf Kalendertermine von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfläche „Trennen“, um den Zugriff auf die Kalendertermine zu stoppen.",
"title": "Kalender"
},
"chatWithPermissions": "Chat mit Freigaben",
"desktopShareFramerate": "Framerate für Bildschirmfreigabe",
"desktopShareHighFpsWarning": "Eine höhere Framerate könnte sich auf Ihre Datenrate auswirken. Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
"desktopShareWarning": "Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
@@ -1152,7 +1132,6 @@
"selectMic": "Mikrofon",
"selfView": "Eigene Ansicht",
"shortcuts": "Tastaturkürzel",
"showSubtitlesOnStage": "Untertitel in Hauptansicht anzeigen",
"speakers": "Lautsprecher",
"startAudioMuted": "Alle Personen treten stummgeschaltet bei",
"startReactionsMuted": "Interaktionstöne für alle deaktivieren",
@@ -1206,13 +1185,11 @@
"fearful": "Ängstlich",
"happy": "Fröhlich",
"hours": "{{count}} Std. ",
"labelTooltip": "Anzahl der Personen: {{count}}",
"minutes": "{{count}} Min. ",
"name": "Name",
"neutral": "Neutral",
"sad": "Traurig",
"search": "Suche",
"searchDescription": "Tippen Sie um die Anwesendenliste zu filtern",
"searchHint": "Suche Anwesende",
"seconds": "{{count}} Sek.",
"speakerStats": "Sprechstatistik",
@@ -1249,7 +1226,6 @@
"closeChat": "Chat schließen",
"closeMoreActions": "„Weitere Einstellungen“ schließen",
"closeParticipantsPane": "Anwesendenliste schließen",
"closedCaptions": "Untertitel",
"collapse": "Einklappen",
"document": "Geteiltes Dokument schließen",
"documentClose": "Geteiltes Dokument schließen",
@@ -1292,7 +1268,7 @@
"muteGUMPending": "Verbinde Ihr Mikrofon",
"noiseSuppression": "Rauschunterdrückung",
"openChat": "Chat öffnen",
"participants": "Anwesenheitsliste öffnen. {{participantsCount}} anwesend",
"participants": "Anwesende",
"pip": "Bild-in-Bild-Modus ein-/ausschalten",
"privateMessage": "Private Nachricht senden",
"profile": "Profil bearbeiten",
@@ -1340,7 +1316,6 @@
"closeChat": "Chat schließen",
"closeParticipantsPane": "Anwesenheitsliste schließen",
"closeReactionsMenu": "Interaktionsmenü schließen",
"closedCaptions": "Untertitel",
"disableNoiseSuppression": "Rauschunterdrückung deaktivieren",
"disableReactionSounds": "Sie können die Interaktionstöne für diese Konferenz deaktivieren",
"documentClose": "Geteiltes Dokument schließen",
@@ -1431,18 +1406,14 @@
"ccButtonTooltip": "Untertitel ein-/ausschalten",
"expandedLabel": "Transkribieren ist derzeit eingeschaltet",
"failed": "Transkribieren fehlgeschlagen",
"labelTooltip": "Die Konferenz wird transkribiert",
"labelTooltipExtra": "Zusätzlich wird das Transkript später verfügbar sein.",
"openClosedCaptions": "Untertitel öffnen",
"original": "Original",
"labelToolTip": "Die Konferenz wird transkribiert",
"sourceLanguageDesc": "Aktuell ist die Sprache der Konferenz auf <b>{{sourceLanguage}}</b> eingestellt. <br/> Sie könne dies hier ",
"sourceLanguageHere": "ändern",
"start": "Anzeige der Untertitel starten",
"stop": "Anzeige der Untertitel stoppen",
"subtitles": "Untertitel",
"subtitlesOff": "Ausschalten",
"tr": "TR",
"translateTo": "Übersetzen in"
"tr": "TR"
},
"unpinParticipant": "{{participantName}} - Nicht mehr anheften",
"userMedia": {

View File

@@ -845,6 +845,7 @@
"or": "abo",
"premeeting": "naglěd",
"screenSharingError": "zmólenje pśi sobuźělenju monitora:",
"showScreen": "naglěd konferency aktiwěrowaś",
"startWithPhone": "zachopiś z telefonowym audio",
"videoOnlyError": "zmólenje wideo:",
"videoTrackError": "Sćažka wideo njejo mógła se załožyś.",
@@ -915,7 +916,7 @@
"localRecordingStartWarningTitle": "nagrawanje dokóńcowaś a zachowaś",
"localRecordingVideoStop": "Gaž kameru wušaltujośo, ga teke lokalne nagrawanje se dokóńcujo. Sćo-li napšawdu wěste, až to cośo?",
"localRecordingVideoWarning": "Aby bildu ze swójeje samskeje kamery nagrawali, musyśo swóju kameru na zachopjeńku nagrawanja zašaltowaś.",
"localRecordingWarning": "Pśespytujśo, lěc sćo aktuelnu kórtu wuzwólili, aby wideo a zuk nagrawali.",
"localRecordingWarning": "Pśespytujśo, lěc sćo aktuelnu kórtu wuzwólili, aby wideo a zuk nagrawali. Dłujkosć nagrawanja jo tuchylu na 1 GB wobgranicowana, což dosega za jadnab 100 minutow.",
"loggedIn": "ako {{userName}} zalogowany/-a",
"noMicPermission": "",
"noStreams": "Žeden zuk a žeden wideo njejo namakany.",

Some files were not shown because too many files have changed in this diff Show More