Compare commits
271 Commits
android-sd
...
4963
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
153f991097 | ||
|
|
79497cecba | ||
|
|
f44faa8d81 | ||
|
|
7ef22603fc | ||
|
|
f030706a94 | ||
|
|
6eac19a058 | ||
|
|
8cffa5553d | ||
|
|
0fcdb6f248 | ||
|
|
399fac78f5 | ||
|
|
8bd874ca70 | ||
|
|
8477ae8daa | ||
|
|
b83bc50c03 | ||
|
|
9bffe149d3 | ||
|
|
1898e4a768 | ||
|
|
d7639963d3 | ||
|
|
f68fe2d083 | ||
|
|
24bf5a2dc3 | ||
|
|
6ee868032e | ||
|
|
cf37d34923 | ||
|
|
f187923233 | ||
|
|
ce6ebca90f | ||
|
|
546df558e3 | ||
|
|
02ec30b8ff | ||
|
|
0d127b30df | ||
|
|
fc78cd0d71 | ||
|
|
f377455069 | ||
|
|
b536aa035c | ||
|
|
d3b18a281a | ||
|
|
cb9c85e1bc | ||
|
|
ff44ff9026 | ||
|
|
3048ce4345 | ||
|
|
0ef6db51d6 | ||
|
|
d96bb83496 | ||
|
|
f4c8310ea7 | ||
|
|
9856add282 | ||
|
|
98658f573c | ||
|
|
20a62e5eb4 | ||
|
|
77890fc27a | ||
|
|
433e212e20 | ||
|
|
527d022d63 | ||
|
|
9724bb1799 | ||
|
|
6398b4ec89 | ||
|
|
d8e5b48aeb | ||
|
|
477d94497b | ||
|
|
d014a52ab3 | ||
|
|
6efa94541e | ||
|
|
f0f9c02452 | ||
|
|
ef4af415a8 | ||
|
|
c6fd8c2bcb | ||
|
|
7d1c8da827 | ||
|
|
9e6939d25f | ||
|
|
c765e08aa1 | ||
|
|
41939d99c8 | ||
|
|
9d0c6e3741 | ||
|
|
572b99b208 | ||
|
|
64ab813b55 | ||
|
|
67ac48cac6 | ||
|
|
afbd29f4a2 | ||
|
|
996c9fb064 | ||
|
|
b53ad353cb | ||
|
|
e0da67dff5 | ||
|
|
dcd073b407 | ||
|
|
c12c554138 | ||
|
|
289ba6f764 | ||
|
|
59afafdf7c | ||
|
|
b74c8b5d1f | ||
|
|
0aef918c55 | ||
|
|
dc3f64fe7a | ||
|
|
cbeb7b86cc | ||
|
|
b1833fddad | ||
|
|
1b2f64efb3 | ||
|
|
7121b2f1e1 | ||
|
|
6c4652e3a0 | ||
|
|
a256c6b8e7 | ||
|
|
96e886d306 | ||
|
|
12552766ce | ||
|
|
299674508b | ||
|
|
58be0f6914 | ||
|
|
529b182666 | ||
|
|
067ff0729e | ||
|
|
6d3d65da03 | ||
|
|
fd4819aeca | ||
|
|
7ca04ccb0f | ||
|
|
bf3726cb93 | ||
|
|
524af5ca67 | ||
|
|
af28080058 | ||
|
|
927b40ec71 | ||
|
|
3bbfdb2846 | ||
|
|
e38ebc6628 | ||
|
|
62cf3099a7 | ||
|
|
b135e2a06a | ||
|
|
7656985fe1 | ||
|
|
5599454ea9 | ||
|
|
807a5ab893 | ||
|
|
c4766125bb | ||
|
|
ba41745d1e | ||
|
|
8eed42c273 | ||
|
|
e803e8cfd9 | ||
|
|
86dd35b927 | ||
|
|
32ecd6310c | ||
|
|
e5277deed5 | ||
|
|
8b315846b9 | ||
|
|
c687f41a89 | ||
|
|
31c0ba4481 | ||
|
|
fc3a743372 | ||
|
|
8b038716a5 | ||
|
|
9662b2ae67 | ||
|
|
6275439a91 | ||
|
|
d9693117f2 | ||
|
|
21382ea6d5 | ||
|
|
6df67694d1 | ||
|
|
08756bc6d0 | ||
|
|
1b1d650b75 | ||
|
|
b1eff72394 | ||
|
|
357bbd1158 | ||
|
|
0ca47e9ffb | ||
|
|
1123b4f2fe | ||
|
|
1224597ede | ||
|
|
58b7663a97 | ||
|
|
cf8ab5e13b | ||
|
|
f99c919416 | ||
|
|
ae21a09bd6 | ||
|
|
39011d8fd3 | ||
|
|
77f1a24344 | ||
|
|
3453e49182 | ||
|
|
b1d7debfb9 | ||
|
|
b826fc1d5a | ||
|
|
c5626e99e9 | ||
|
|
ae28fcc12f | ||
|
|
987760abbd | ||
|
|
a3b364f8d7 | ||
|
|
2ea317d721 | ||
|
|
eb41a306a6 | ||
|
|
3426290bf2 | ||
|
|
dfd33521bf | ||
|
|
be3bc75403 | ||
|
|
4621fad832 | ||
|
|
e4b34e1c89 | ||
|
|
0067f6b077 | ||
|
|
989044b3a9 | ||
|
|
a78ca5fcad | ||
|
|
1ad40de487 | ||
|
|
2c9078985f | ||
|
|
77ee4b13e1 | ||
|
|
a3a2ce3875 | ||
|
|
e0c77dcd95 | ||
|
|
e035d33fa9 | ||
|
|
11202595bd | ||
|
|
415670e24b | ||
|
|
05f3b4390d | ||
|
|
cff0a619f5 | ||
|
|
f7c0d4f1fe | ||
|
|
8fccb05519 | ||
|
|
b4155ab6d2 | ||
|
|
a1d3870634 | ||
|
|
07f16a7a51 | ||
|
|
0e7bde2ff0 | ||
|
|
e9d00acad8 | ||
|
|
911aaed052 | ||
|
|
5fd9dc74e4 | ||
|
|
eb68467e15 | ||
|
|
6a5d6afc94 | ||
|
|
2a9b6a7d28 | ||
|
|
67beafc9af | ||
|
|
6175a5cad5 | ||
|
|
678f3e232b | ||
|
|
f3c1b8ac08 | ||
|
|
f225ce886f | ||
|
|
6a4417c6cc | ||
|
|
6b66c8dd20 | ||
|
|
d3680bbebd | ||
|
|
7933d4b4d6 | ||
|
|
e7297714c6 | ||
|
|
8da154b185 | ||
|
|
3c94a5ccfd | ||
|
|
78d4af6bf2 | ||
|
|
33fc3833f9 | ||
|
|
b179542c39 | ||
|
|
49c38a73aa | ||
|
|
fc27300132 | ||
|
|
57ecdff9eb | ||
|
|
effa878fa4 | ||
|
|
9d4e49a5af | ||
|
|
6b4d25c0d3 | ||
|
|
2f5ab2757f | ||
|
|
bde26c4fbb | ||
|
|
68c2c9be40 | ||
|
|
5b21051c6b | ||
|
|
8806269af0 | ||
|
|
3a8bd852b2 | ||
|
|
f50872285d | ||
|
|
e937e99284 | ||
|
|
3972e076f0 | ||
|
|
81cf79e643 | ||
|
|
a22d054b10 | ||
|
|
7fce181080 | ||
|
|
92735478d1 | ||
|
|
7dabfc21b4 | ||
|
|
1395f84550 | ||
|
|
d080460f9b | ||
|
|
61567f47c0 | ||
|
|
4f3058eae2 | ||
|
|
3a073d9af4 | ||
|
|
aef0287605 | ||
|
|
bb19567efa | ||
|
|
79ab973694 | ||
|
|
7046785ca3 | ||
|
|
b817bd19d5 | ||
|
|
817d54b0b9 | ||
|
|
3f0bb6818c | ||
|
|
4fa47c8070 | ||
|
|
0dcb8a025b | ||
|
|
8defaa9aec | ||
|
|
d214079148 | ||
|
|
096ee3cb53 | ||
|
|
fd606896b8 | ||
|
|
226581a81a | ||
|
|
e1c5b1e626 | ||
|
|
831c5ba59d | ||
|
|
bad1bc91cf | ||
|
|
30d0aabaca | ||
|
|
22b6d32174 | ||
|
|
31ace267ce | ||
|
|
194d357005 | ||
|
|
c2ad06c5e6 | ||
|
|
e40b02ab3c | ||
|
|
2587eefefc | ||
|
|
b87c433e99 | ||
|
|
751644db16 | ||
|
|
c508572cc5 | ||
|
|
b86c271a80 | ||
|
|
5efbe5f0ec | ||
|
|
2784c43a1b | ||
|
|
f5a34183e9 | ||
|
|
29f5d87d77 | ||
|
|
ab6790bdaa | ||
|
|
2e308d67d8 | ||
|
|
91ba835f78 | ||
|
|
2643029ac8 | ||
|
|
e40e078a29 | ||
|
|
6df5a4cf31 | ||
|
|
c7c7d7a155 | ||
|
|
8f06866646 | ||
|
|
b559cb8ec6 | ||
|
|
30a2e84da1 | ||
|
|
3122983000 | ||
|
|
407021e258 | ||
|
|
1a62a7b1cc | ||
|
|
0ee03f1538 | ||
|
|
572beb8382 | ||
|
|
d0d32b8a19 | ||
|
|
82ff988c18 | ||
|
|
8fa5d09612 | ||
|
|
508f1e0da9 | ||
|
|
dcda89012e | ||
|
|
d93a402cc2 | ||
|
|
b7b260f4c9 | ||
|
|
4db3f04c0c | ||
|
|
126a2bd0d7 | ||
|
|
29bbcf8590 | ||
|
|
5c46b03251 | ||
|
|
eeb5abbbe8 | ||
|
|
49583b611c | ||
|
|
9e29dd063f | ||
|
|
a2e2d31dfd | ||
|
|
62c06441b1 | ||
|
|
f718a3e050 | ||
|
|
899968d3a9 | ||
|
|
696f509f18 | ||
|
|
430591bd1e | ||
|
|
8ee324b37f |
@@ -6,7 +6,8 @@ build/*
|
||||
flow-typed/*
|
||||
libs/*
|
||||
resources/*
|
||||
react/features/stream-effects/blur/vendor/*
|
||||
react/features/stream-effects/virtual-background/vendor/*
|
||||
load-test/*
|
||||
|
||||
# ESLint will by default ignore its own configuration file. However, there does
|
||||
# not seem to be a reason why we will want to risk being inconsistent with our
|
||||
|
||||
1
.github/workflows/ci.yml
vendored
@@ -12,5 +12,6 @@ jobs:
|
||||
with:
|
||||
node-version: '12.x'
|
||||
- run: npm install
|
||||
- run: git status -s --untracked-files=no
|
||||
- run: npm run lint
|
||||
- run: make
|
||||
|
||||
8
Makefile
@@ -5,8 +5,8 @@ LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
|
||||
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
|
||||
OLM_DIR = node_modules/olm
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||
TFLITE_WASM = react/features/stream-effects/blur/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/blur/vendor/models/
|
||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
|
||||
NODE_SASS = ./node_modules/.bin/sass
|
||||
NPM = npm
|
||||
OUTPUT_DIR = .
|
||||
@@ -51,10 +51,6 @@ deploy-appbundle:
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.map \
|
||||
$(BUILD_DIR)/video-blur-effect.min.js \
|
||||
$(BUILD_DIR)/video-blur-effect.min.map \
|
||||
$(BUILD_DIR)/rnnoise-processor.min.js \
|
||||
$(BUILD_DIR)/rnnoise-processor.min.map \
|
||||
$(BUILD_DIR)/close3.min.js \
|
||||
$(BUILD_DIR)/close3.min.map \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
@@ -122,7 +122,7 @@ gradle.projectsEvaluated {
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
output.getProcessManifestProvider().get().doLast {
|
||||
def outputDir = manifestOutputDirectory.get().asFile
|
||||
def outputDir = multiApkManifestOutputDirectory.get().asFile
|
||||
def manifestPath = new File(outputDir, 'AndroidManifest.xml')
|
||||
def charset = 'UTF-8'
|
||||
def text
|
||||
|
||||
@@ -25,5 +25,5 @@ android.enableDexingArtifactTransform.desugaring=false
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
appVersion=21.0.0
|
||||
sdkVersion=3.2.0
|
||||
appVersion=21.2.0
|
||||
sdkVersion=3.5.0
|
||||
|
||||
@@ -70,7 +70,6 @@ dependencies {
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-linear-gradient')
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-webrtc')
|
||||
|
||||
@@ -36,8 +36,17 @@ public class BroadcastAction {
|
||||
|
||||
for (String key : this.data.keySet()) {
|
||||
try {
|
||||
// TODO add support for different types of objects
|
||||
nativeMap.putString(key, this.data.get(key).toString());
|
||||
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);
|
||||
}
|
||||
@@ -66,7 +75,8 @@ public class BroadcastAction {
|
||||
RETRIEVE_PARTICIPANTS_INFO("org.jitsi.meet.RETRIEVE_PARTICIPANTS_INFO"),
|
||||
OPEN_CHAT("org.jitsi.meet.OPEN_CHAT"),
|
||||
CLOSE_CHAT("org.jitsi.meet.CLOSE_CHAT"),
|
||||
SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE");
|
||||
SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE"),
|
||||
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED");
|
||||
|
||||
private final String action;
|
||||
|
||||
|
||||
@@ -85,7 +85,9 @@ public class BroadcastEvent {
|
||||
SCREEN_SHARE_TOGGLED("org.jitsi.meet.SCREEN_SHARE_TOGGLED"),
|
||||
PARTICIPANTS_INFO_RETRIEVED("org.jitsi.meet.PARTICIPANTS_INFO_RETRIEVED"),
|
||||
CHAT_MESSAGE_RECEIVED("org.jitsi.meet.CHAT_MESSAGE_RECEIVED"),
|
||||
CHAT_TOGGLED("org.jitsi.meet.CHAT_TOGGLED");
|
||||
CHAT_TOGGLED("org.jitsi.meet.CHAT_TOGGLED"),
|
||||
VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED");
|
||||
|
||||
|
||||
private static final String CONFERENCE_WILL_JOIN_NAME = "CONFERENCE_WILL_JOIN";
|
||||
private static final String CONFERENCE_JOINED_NAME = "CONFERENCE_JOINED";
|
||||
@@ -98,6 +100,7 @@ public class BroadcastEvent {
|
||||
private static final String PARTICIPANTS_INFO_RETRIEVED_NAME = "PARTICIPANTS_INFO_RETRIEVED";
|
||||
private static final String CHAT_MESSAGE_RECEIVED_NAME = "CHAT_MESSAGE_RECEIVED";
|
||||
private static final String CHAT_TOGGLED_NAME = "CHAT_TOGGLED";
|
||||
private static final String VIDEO_MUTED_CHANGED_NAME = "VIDEO_MUTED_CHANGED";
|
||||
|
||||
private final String action;
|
||||
|
||||
@@ -142,6 +145,8 @@ public class BroadcastEvent {
|
||||
return CHAT_MESSAGE_RECEIVED;
|
||||
case CHAT_TOGGLED_NAME:
|
||||
return CHAT_TOGGLED;
|
||||
case VIDEO_MUTED_CHANGED_NAME:
|
||||
return VIDEO_MUTED_CHANGED;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -20,8 +20,10 @@ public class BroadcastIntentHelper {
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildToggleScreenShareIntent() {
|
||||
return new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
||||
public static Intent buildToggleScreenShareIntent(boolean enabled) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
||||
intent.putExtra("enabled", enabled);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildOpenChatIntent(String participantId) {
|
||||
@@ -40,4 +42,10 @@ public class BroadcastIntentHelper {
|
||||
intent.putExtra("message", message);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static Intent buildSetVideoMutedIntent(boolean muted) {
|
||||
Intent intent = new Intent(BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
|
||||
intent.putExtra("muted", muted);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ class ExternalAPIModule
|
||||
constants.put("OPEN_CHAT", BroadcastAction.Type.OPEN_CHAT.getAction());
|
||||
constants.put("CLOSE_CHAT", BroadcastAction.Type.CLOSE_CHAT.getAction());
|
||||
constants.put("SEND_CHAT_MESSAGE", BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
|
||||
constants.put("SET_VIDEO_MUTED", BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.facebook.react.modules.core.PermissionListener;
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
|
||||
@@ -58,6 +59,9 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
Intent intent = new Intent(context, JitsiMeetActivity.class);
|
||||
intent.setAction(ACTION_JITSI_MEET_CONFERENCE);
|
||||
intent.putExtra(JITSI_MEET_CONFERENCE_OPTIONS, options);
|
||||
if (!(context instanceof Activity)) {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||
if (conferenceUrl == null) {
|
||||
stopSelf();
|
||||
OngoingNotification.resetStartingtime();
|
||||
JitsiMeetLogger.i(TAG + "Service stopped");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ class OngoingNotification {
|
||||
private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
private static long startingTime = 0;
|
||||
|
||||
static void createOngoingConferenceNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
@@ -85,6 +86,10 @@ class OngoingNotification {
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
|
||||
|
||||
if (startingTime == 0) {
|
||||
startingTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
builder
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setContentTitle(context.getString(R.string.ongoing_notification_title))
|
||||
@@ -92,6 +97,8 @@ class OngoingNotification {
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.setWhen(startingTime)
|
||||
.setUsesChronometer(true)
|
||||
.setAutoCancel(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setOnlyAlertOnce(true)
|
||||
@@ -110,6 +117,10 @@ class OngoingNotification {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static void resetStartingtime() {
|
||||
startingTime = 0;
|
||||
}
|
||||
|
||||
private static NotificationCompat.Action createAction(Context context, JitsiMeetOngoingConferenceService.Action action, @StringRes int titleId) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
intent.setAction(action.getName());
|
||||
|
||||
@@ -178,7 +178,6 @@ class ReactInstanceManagerHolder {
|
||||
|
||||
List<ReactPackage> packages
|
||||
= new ArrayList<>(Arrays.asList(
|
||||
new com.BV.LinearGradient.LinearGradientPackage(),
|
||||
new com.calendarevents.CalendarEventsPackage(),
|
||||
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||
new com.facebook.react.shell.MainReactPackage(),
|
||||
|
||||
@@ -19,8 +19,6 @@ include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
|
||||
include ':react-native-linear-gradient'
|
||||
project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')
|
||||
include ':react-native-sound'
|
||||
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
|
||||
include ':react-native-splash-screen'
|
||||
|
||||
2
app.js
@@ -1,8 +1,6 @@
|
||||
/* application specific logic */
|
||||
|
||||
import 'jquery';
|
||||
import 'jquery-contextmenu';
|
||||
import 'jQuery-Impromptu';
|
||||
|
||||
import 'olm';
|
||||
|
||||
|
||||
194
conference.js
@@ -1,10 +1,12 @@
|
||||
/* global APP, JitsiMeetJS, config, interfaceConfig */
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import EventEmitter from 'events';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { openConnection } from './connection';
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
@@ -99,6 +101,7 @@ import {
|
||||
destroyLocalTracks,
|
||||
getLocalJitsiAudioTrack,
|
||||
getLocalJitsiVideoTrack,
|
||||
getLocalTracks,
|
||||
isLocalCameraTrackMuted,
|
||||
isLocalTrackMuted,
|
||||
isUserInteractionRequiredForUnmute,
|
||||
@@ -124,8 +127,9 @@ import {
|
||||
makePrecallTest
|
||||
} from './react/features/prejoin';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
||||
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
||||
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video/actions';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
@@ -377,7 +381,6 @@ class ConferenceConnector {
|
||||
if (this.reconnectTimeout !== null) {
|
||||
clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
AuthHandler.closeAuth();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,7 +395,8 @@ class ConferenceConnector {
|
||||
*
|
||||
*/
|
||||
connect() {
|
||||
room.join();
|
||||
// the local storage overrides here and in connection.js can be used by jibri
|
||||
room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,18 +477,13 @@ export default {
|
||||
*/
|
||||
createInitialLocalTracks(options = {}) {
|
||||
const errors = {};
|
||||
|
||||
// Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
|
||||
// "Are you trying to speak?" ) even if the user joins the conference muted.
|
||||
const initialDevices = config.disableInitialGUM ? [] : [ 'audio' ];
|
||||
const requestedAudio = !config.disableInitialGUM;
|
||||
let requestedVideo = false;
|
||||
|
||||
// Always get a handle on the audio input device so that we have statistics even if the user joins the
|
||||
// conference muted. Previous implementation would only acquire the handle when the user first unmuted,
|
||||
// which would results in statistics ( such as "No audio input" or "Are you trying to speak?") being available
|
||||
// only after that point.
|
||||
if (options.startWithAudioMuted) {
|
||||
this.muteAudio(true, true);
|
||||
}
|
||||
|
||||
if (!config.disableInitialGUM
|
||||
&& !options.startWithVideoMuted
|
||||
&& !options.startAudioOnly
|
||||
@@ -510,7 +509,7 @@ export default {
|
||||
let tryCreateLocalTracks;
|
||||
|
||||
// On Electron there is no permission prompt for granting permissions. That's why we don't need to
|
||||
// spend much time displaying the overlay screen. If GUM is not resolved withing 15 seconds it will
|
||||
// spend much time displaying the overlay screen. If GUM is not resolved within 15 seconds it will
|
||||
// probably never resolve.
|
||||
const timeout = browser.isElectron() ? 15000 : 60000;
|
||||
|
||||
@@ -572,7 +571,7 @@ export default {
|
||||
|
||||
if (err.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
|
||||
// In this case we expect that the permission prompt is still visible. There is no point of
|
||||
// executing GUM with different source. Also at the time of writting the following
|
||||
// executing GUM with different source. Also at the time of writing the following
|
||||
// inconsistency have been noticed in some browsers - if the permissions prompt is visible
|
||||
// and another GUM is executed the prompt does not change its content but if the user
|
||||
// clicks allow the user action isassociated with the latest GUM call.
|
||||
@@ -629,7 +628,7 @@ export default {
|
||||
|
||||
// Hide the permissions prompt/overlay as soon as the tracks are
|
||||
// created. Don't wait for the connection to be made, since in some
|
||||
// cases, when auth is rquired, for instance, that won't happen until
|
||||
// cases, when auth is required, for instance, that won't happen until
|
||||
// the user inputs their credentials, but the dialog would be
|
||||
// overshadowed by the overlay.
|
||||
tryCreateLocalTracks.then(tracks => {
|
||||
@@ -824,12 +823,16 @@ export default {
|
||||
return this._setLocalAudioVideoStreams(tracks);
|
||||
}
|
||||
|
||||
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(
|
||||
roomName, initialOptions);
|
||||
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(roomName, initialOptions);
|
||||
let localTracks = tracks;
|
||||
|
||||
this._initDeviceList(true);
|
||||
|
||||
return this.startConference(con, tracks);
|
||||
if (initialOptions.startWithAudioMuted) {
|
||||
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
|
||||
}
|
||||
|
||||
return this.startConference(con, localTracks);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1020,7 +1023,11 @@ export default {
|
||||
// Rollback the video muted status by using null track
|
||||
return null;
|
||||
})
|
||||
.then(videoTrack => this.useVideoStream(videoTrack));
|
||||
.then(videoTrack => {
|
||||
logger.debug(`muteVideo: calling useVideoStream for track: ${videoTrack}`);
|
||||
|
||||
return this.useVideoStream(videoTrack);
|
||||
});
|
||||
} else {
|
||||
// FIXME show error dialog if it fails (should be handled by react)
|
||||
muteLocalVideo(mute);
|
||||
@@ -1320,7 +1327,11 @@ export default {
|
||||
this._getConferenceOptions());
|
||||
|
||||
APP.store.dispatch(conferenceWillJoin(room));
|
||||
this._setLocalAudioVideoStreams(localTracks);
|
||||
|
||||
// Filter out the tracks that are muted.
|
||||
const tracks = localTracks.filter(track => !track.isMuted());
|
||||
|
||||
this._setLocalAudioVideoStreams(tracks);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
sendLocalParticipant(APP.store, room);
|
||||
@@ -1339,8 +1350,11 @@ export default {
|
||||
if (track.isAudioTrack()) {
|
||||
return this.useAudioStream(track);
|
||||
} else if (track.isVideoTrack()) {
|
||||
logger.debug(`_setLocalAudioVideoStreams is calling useVideoStream with track: ${track}`);
|
||||
|
||||
return this.useVideoStream(track);
|
||||
}
|
||||
|
||||
logger.error(
|
||||
'Ignored not an audio nor a video track: ', track);
|
||||
|
||||
@@ -1360,6 +1374,8 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
useVideoStream(newTrack) {
|
||||
logger.debug(`useVideoStream: ${newTrack}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
|
||||
const state = APP.store.getState();
|
||||
@@ -1369,24 +1385,30 @@ export default {
|
||||
if (isPrejoinPageVisible(state)) {
|
||||
const oldTrack = getLocalJitsiVideoTrack(state);
|
||||
|
||||
logger.debug(`useVideoStream on the prejoin screen: Replacing ${oldTrack} with ${newTrack}`);
|
||||
|
||||
return APP.store.dispatch(replaceLocalTrack(oldTrack, newTrack))
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.catch(error => {
|
||||
logger.error(`useVideoStream failed on the prejoin screen: ${error}`);
|
||||
reject(error);
|
||||
})
|
||||
.then(onFinish);
|
||||
}
|
||||
|
||||
logger.debug(`useVideoStream: Replacing ${this.localVideo} with ${newTrack}`);
|
||||
APP.store.dispatch(
|
||||
replaceLocalTrack(this.localVideo, newTrack, room))
|
||||
replaceLocalTrack(this.localVideo, newTrack, room))
|
||||
.then(() => {
|
||||
this.localVideo = newTrack;
|
||||
this._setSharingScreen(newTrack);
|
||||
if (newTrack) {
|
||||
APP.UI.addLocalVideoStream(newTrack);
|
||||
}
|
||||
this.setVideoMuteStatus(this.isLocalVideoMuted());
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.catch(error => {
|
||||
logger.error(`useVideoStream failed: ${error}`);
|
||||
reject(error);
|
||||
})
|
||||
.then(onFinish);
|
||||
});
|
||||
});
|
||||
@@ -1531,9 +1553,15 @@ export default {
|
||||
this._desktopAudioStream = undefined;
|
||||
}
|
||||
|
||||
APP.store.dispatch(setScreenAudioShareState(false));
|
||||
|
||||
if (didHaveVideo) {
|
||||
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
|
||||
.then(([ stream ]) => this.useVideoStream(stream))
|
||||
.then(([ stream ]) => {
|
||||
logger.debug(`_turnScreenSharingOff using ${stream} for useVideoStream`);
|
||||
|
||||
return this.useVideoStream(stream);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('failed to switch back to local video', error);
|
||||
|
||||
@@ -1544,7 +1572,11 @@ export default {
|
||||
);
|
||||
});
|
||||
} else {
|
||||
promise = promise.then(() => this.useVideoStream(null));
|
||||
promise = promise.then(() => {
|
||||
logger.debug('_turnScreenSharingOff using null for useVideoStream');
|
||||
|
||||
return this.useVideoStream(null);
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(
|
||||
@@ -1555,6 +1587,8 @@ export default {
|
||||
},
|
||||
error => {
|
||||
this.videoSwitchInProgress = false;
|
||||
logger.error(`_turnScreenSharingOff failed: ${error}`);
|
||||
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
@@ -1575,6 +1609,7 @@ export default {
|
||||
* @return {Promise.<T>}
|
||||
*/
|
||||
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
|
||||
logger.debug(`toggleScreenSharing: ${toggle}`);
|
||||
if (this.videoSwitchInProgress) {
|
||||
return Promise.reject('Switch in progress.');
|
||||
}
|
||||
@@ -1636,11 +1671,30 @@ export default {
|
||||
= this._turnScreenSharingOff.bind(this, didHaveVideo);
|
||||
|
||||
const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
const dekstopAudioStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (dekstopAudioStream) {
|
||||
dekstopAudioStream.on(
|
||||
JitsiTrackEvents.LOCAL_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
|
||||
// be reached.
|
||||
isScreenAudioShared(APP.store.getState())
|
||||
&& this._untoggleScreenSharing
|
||||
&& this._untoggleScreenSharing();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1717,16 +1771,12 @@ export default {
|
||||
const isPortrait = height >= width;
|
||||
const DESKTOP_STREAM_CAP = 720;
|
||||
|
||||
// Config.js setting for resizing high resolution desktop tracks to 720p when presenter is turned on.
|
||||
const resizeEnabled = config.videoQuality && config.videoQuality.resizeDesktopForPresenter;
|
||||
const highResolutionTrack
|
||||
= (isPortrait && width > DESKTOP_STREAM_CAP) || (!isPortrait && height > DESKTOP_STREAM_CAP);
|
||||
|
||||
// Resizing the desktop track for presenter is causing blurriness of the desktop share on chrome.
|
||||
// Disable resizing by default, enable it only when config.js setting is enabled.
|
||||
// Firefox doesn't return width and height for desktop tracks. Therefore, track needs to be resized
|
||||
// for creating the canvas for presenter.
|
||||
const resizeDesktopStream = browser.isFirefox() || (highResolutionTrack && resizeEnabled);
|
||||
const resizeDesktopStream = highResolutionTrack && config.videoQuality?.resizeDesktopForPresenter;
|
||||
|
||||
if (resizeDesktopStream) {
|
||||
let desktopResizeConstraints = {};
|
||||
@@ -1744,7 +1794,7 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
// Apply the contraints on the desktop track.
|
||||
// Apply the constraints on the desktop track.
|
||||
try {
|
||||
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
|
||||
} catch (err) {
|
||||
@@ -1802,13 +1852,28 @@ export default {
|
||||
|
||||
return this._createDesktopTrack(options)
|
||||
.then(async streams => {
|
||||
const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
let desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
const { audioOnly = false } = options;
|
||||
|
||||
// If we're in audio only mode dispose of the video track otherwise the screensharing state will be
|
||||
// inconsistent.
|
||||
if (audioOnly) {
|
||||
desktopVideoStream.dispose();
|
||||
desktopVideoStream = undefined;
|
||||
|
||||
if (!this._desktopAudioStream) {
|
||||
return Promise.reject(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK);
|
||||
}
|
||||
}
|
||||
|
||||
if (desktopVideoStream) {
|
||||
logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
|
||||
await this.useVideoStream(desktopVideoStream);
|
||||
}
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (this._desktopAudioStream) {
|
||||
// If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
|
||||
@@ -1821,7 +1886,9 @@ export default {
|
||||
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
|
||||
// stream as we would use a regular stream.
|
||||
await this.useAudioStream(this._desktopAudioStream);
|
||||
|
||||
}
|
||||
APP.store.dispatch(setScreenAudioShareState(true));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
@@ -1889,6 +1956,9 @@ export default {
|
||||
} else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
|
||||
descriptionKey = 'dialog.screenSharingFailed';
|
||||
titleKey = 'dialog.screenSharingFailedTitle';
|
||||
} else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
|
||||
descriptionKey = 'notify.screenShareNoAudio';
|
||||
titleKey = 'notify.screenShareNoAudioTitle';
|
||||
}
|
||||
|
||||
APP.UI.messageHandler.showError({
|
||||
@@ -1934,7 +2004,7 @@ export default {
|
||||
}
|
||||
|
||||
APP.store.dispatch(updateRemoteParticipantFeatures(user));
|
||||
logger.log(`USER ${id} connnected:`, user);
|
||||
logger.log(`USER ${id} connected:`, user);
|
||||
APP.UI.addUser(user);
|
||||
});
|
||||
|
||||
@@ -2010,6 +2080,7 @@ export default {
|
||||
if (participantThatMutedUs) {
|
||||
APP.store.dispatch(participantMutedUs(participantThatMutedUs, track));
|
||||
if (this.isSharingScreen && track.isVideoTrack()) {
|
||||
logger.debug('TRACK_MUTE_CHANGED while screen sharing');
|
||||
this._turnScreenSharingOff(false);
|
||||
}
|
||||
}
|
||||
@@ -2163,8 +2234,26 @@ export default {
|
||||
}
|
||||
);
|
||||
room.on(JitsiConferenceEvents.STARTED_MUTED, () => {
|
||||
(room.isStartAudioMuted() || room.isStartVideoMuted())
|
||||
&& APP.UI.notifyInitiallyMuted();
|
||||
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) {
|
||||
if (audioMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.AUDIO) {
|
||||
promises.push(this.useAudioStream(null));
|
||||
}
|
||||
if (videoMuted && track.jitsiTrack?.getType() === MEDIA_TYPE.VIDEO) {
|
||||
promises.push(this.useVideoStream(null));
|
||||
}
|
||||
}
|
||||
|
||||
Promise.allSettled(promises)
|
||||
.then(() => APP.UI.notifyInitiallyMuted());
|
||||
});
|
||||
|
||||
room.on(
|
||||
@@ -2215,7 +2304,7 @@ export default {
|
||||
.then(effect => this.localVideo.setEffect(effect))
|
||||
.then(() => {
|
||||
this.setVideoMuteStatus(false);
|
||||
logger.log('switched local video device');
|
||||
logger.log('Switched local video device while screen sharing and the video is unmuted');
|
||||
this._updateVideoDeviceId();
|
||||
})
|
||||
.catch(err => APP.store.dispatch(notifyCameraError(err)));
|
||||
@@ -2224,7 +2313,7 @@ export default {
|
||||
// id for video, dispose the existing presenter track and create a new effect
|
||||
// that can be applied on un-mute.
|
||||
} else if (this.isSharingScreen && videoWasMuted) {
|
||||
logger.log('switched local video device');
|
||||
logger.log('Switched local video device: while screen sharing and the video is muted');
|
||||
const { height } = this.localVideo.track.getSettings();
|
||||
|
||||
this._updateVideoDeviceId();
|
||||
@@ -2251,12 +2340,20 @@ export default {
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(stream => this.useVideoStream(stream))
|
||||
.then(stream => {
|
||||
logger.log('Switching the local video device.');
|
||||
|
||||
return this.useVideoStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
logger.log('switched local video device');
|
||||
logger.log('Switched local video device.');
|
||||
this._updateVideoDeviceId();
|
||||
})
|
||||
.catch(err => APP.store.dispatch(notifyCameraError(err)));
|
||||
.catch(error => {
|
||||
logger.error(`Switching the local video device failed: ${error}`);
|
||||
|
||||
return APP.store.dispatch(notifyCameraError(error));
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -2345,11 +2442,17 @@ export default {
|
||||
// There is no guarantee another event will trigger the update
|
||||
// immediately and in all situations, for example because a remote
|
||||
// participant is having connection trouble so no status changes.
|
||||
APP.UI.updateAllVideos();
|
||||
const displayedUserId = APP.UI.getLargeVideoID();
|
||||
|
||||
if (displayedUserId) {
|
||||
APP.UI.updateLargeVideo(displayedUserId, true);
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
||||
UIEvents.TOGGLE_SCREENSHARING, audioOnly => {
|
||||
this.toggleScreenSharing(undefined, { audioOnly });
|
||||
}
|
||||
);
|
||||
|
||||
/* eslint-disable max-params */
|
||||
@@ -2608,6 +2711,7 @@ export default {
|
||||
delete newDevices.videoinput;
|
||||
|
||||
// Removing the current video track in order to force the unmute to select the preferred device.
|
||||
logger.debug('_onDeviceListChanged: Removing the current video track.');
|
||||
this.useVideoStream(null);
|
||||
|
||||
}
|
||||
|
||||
109
config.js
@@ -59,8 +59,10 @@ var config = {
|
||||
// simulcast is turned off for the desktop share. If presenter is turned
|
||||
// on while screensharing is in progress, the max bitrate is automatically
|
||||
// adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
|
||||
// the probability for this to be enabled.
|
||||
// capScreenshareBitrate: 1 // 0 to disable
|
||||
// the probability for this to be enabled. This setting has been deprecated.
|
||||
// desktopSharingFrameRate.max now determines whether simulcast will be enabled
|
||||
// or disabled for the screenshare.
|
||||
// capScreenshareBitrate: 1 // 0 to disable - deprecated.
|
||||
|
||||
// Enable callstats only for a percentage of users.
|
||||
// This takes a value between 0 and 100 which determines the probability for
|
||||
@@ -117,13 +119,15 @@ var config = {
|
||||
// participants and to enable it back a reload is needed.
|
||||
// startSilent: false
|
||||
|
||||
// Sets the preferred target bitrate for the Opus audio codec by setting its
|
||||
// 'maxaveragebitrate' parameter. Currently not available in p2p mode.
|
||||
// Valid values are in the range 6000 to 510000
|
||||
// opusMaxAverageBitrate: 20000,
|
||||
|
||||
// Enables support for opus-red (redundancy for Opus).
|
||||
// enableOpusRed: false
|
||||
// enableOpusRed: false,
|
||||
|
||||
// Specify audio quality stereo and opusMaxAverageBitrate values in order to enable HD audio.
|
||||
// Beware, by doing so, you are disabling echo cancellation, noise suppression and AGC.
|
||||
// audioQuality: {
|
||||
// stereo: false,
|
||||
// opusMaxAverageBitrate: null // Value to fit the 6000 to 510000 range.
|
||||
// },
|
||||
|
||||
// Video
|
||||
|
||||
@@ -224,6 +228,11 @@ var config = {
|
||||
// Default value for the channel "last N" attribute. -1 for unlimited.
|
||||
channelLastN: -1,
|
||||
|
||||
// Provides a way for the lastN value to be controlled through the UI.
|
||||
// When startLastN is present, conference starts with a last-n value of startLastN and channelLastN
|
||||
// value will be used when the quality level is selected using "Manage Video Quality" slider.
|
||||
// startLastN: 1,
|
||||
|
||||
// Provides a way to use different "last N" values based on the number of participants in the conference.
|
||||
// The keys in an Object represent number of participants and the values are "last N" to be used when number of
|
||||
// participants gets to or above the number.
|
||||
@@ -261,12 +270,24 @@ var config = {
|
||||
// // to take effect.
|
||||
// preferredCodec: 'VP8',
|
||||
//
|
||||
// // Provides a way to enforce the preferred codec for the conference even when the conference has endpoints
|
||||
// // that do not support the preferred codec. For example, older versions of Safari do not support VP9 yet.
|
||||
// // This will result in Safari not being able to decode video from endpoints sending VP9 video.
|
||||
// // When set to false, the conference falls back to VP8 whenever there is an endpoint that doesn't support the
|
||||
// // preferred codec and goes back to the preferred codec when that endpoint leaves.
|
||||
// // enforcePreferredCodec: false,
|
||||
//
|
||||
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
|
||||
// // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
|
||||
// // are the max.bitrates to be set on that particular type of stream. The actual send may vary based on
|
||||
// // the available bandwidth calculated by the browser, but it will be capped by the values specified here.
|
||||
// // This is currently not implemented on app based clients on mobile.
|
||||
// maxBitratesVideo: {
|
||||
// H264: {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000
|
||||
// },
|
||||
// VP8 : {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
@@ -332,8 +353,7 @@ var config = {
|
||||
// enableIceRestart: false,
|
||||
|
||||
// Enables forced reload of the client when the call is migrated as a result of
|
||||
// the bridge going down. Currently enabled by default as call migration through
|
||||
// session-terminate is causing siganling issues when Octo is enabled.
|
||||
// the bridge going down.
|
||||
// enableForcedReload: true,
|
||||
|
||||
// Use TURN/UDP servers for the jitsi-videobridge connection (by default
|
||||
@@ -412,7 +432,26 @@ var config = {
|
||||
// enableAutomaticUrlCopy: false,
|
||||
|
||||
// Base URL for a Gravatar-compatible service. Defaults to libravatar.
|
||||
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/';
|
||||
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/',
|
||||
|
||||
// Moved from interfaceConfig(TOOLBAR_BUTTONS).
|
||||
// The name of the toolbar buttons to display in the toolbar, including the
|
||||
// "More actions" menu. If present, the button will display. Exceptions are
|
||||
// "livestreaming" and "recording" which also require being a moderator and
|
||||
// some other values in config.js to be enabled. Also, the "profile" button will
|
||||
// not display for users with a JWT.
|
||||
// Notes:
|
||||
// - 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: [
|
||||
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
|
||||
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
// ],
|
||||
|
||||
// Stats
|
||||
//
|
||||
@@ -437,6 +476,10 @@ var config = {
|
||||
// Enables sending participants' emails (if available) to callstats and other analytics
|
||||
// enableEmailInStats: false,
|
||||
|
||||
// Controls the percentage of automatic feedback shown to participants when callstats is enabled.
|
||||
// The default value is 100%. If set to 0, no automatic feedback will be requested
|
||||
// feedbackPercentage: 100,
|
||||
|
||||
// Privacy
|
||||
//
|
||||
|
||||
@@ -458,13 +501,6 @@ var config = {
|
||||
// connection.
|
||||
enabled: true,
|
||||
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:3478' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
|
||||
// Sets the ICE transport policy for the p2p connection. At the time
|
||||
// of this writing the list of possible values are 'all' and 'relay',
|
||||
// but that is subject to change in the future. The enum is defined in
|
||||
@@ -475,7 +511,7 @@ var config = {
|
||||
|
||||
// If set to true, it will prefer to use H.264 for P2P calls (if H.264
|
||||
// is supported). This setting is deprecated, use preferredCodec instead.
|
||||
// preferH264: true
|
||||
// preferH264: true,
|
||||
|
||||
// Provides a way to set the video codec preference on the p2p connection. Acceptable
|
||||
// codec values are 'VP8', 'VP9' and 'H264'.
|
||||
@@ -490,7 +526,14 @@ var config = {
|
||||
|
||||
// How long we're going to wait, before going back to P2P after the 3rd
|
||||
// participant has left the conference (to filter out page reload).
|
||||
// backToP2PDelay: 5
|
||||
// backToP2PDelay: 5,
|
||||
|
||||
// The STUN servers that will be used in the peer to peer connections
|
||||
stunServers: [
|
||||
|
||||
// { urls: 'stun:jitsi-meet.example.com:3478' },
|
||||
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
|
||||
]
|
||||
},
|
||||
|
||||
analytics: {
|
||||
@@ -517,7 +560,7 @@ var config = {
|
||||
// The interval at which rtcstats will poll getStats, defaults to 1000ms.
|
||||
// If the value is set to 0 getStats won't be polled and the rtcstats client
|
||||
// will only send data related to RTCPeerConnection events.
|
||||
// rtcstatsPolIInterval: 1000
|
||||
// rtcstatsPolIInterval: 1000,
|
||||
|
||||
// Array of script URLs to load as lib-jitsi-meet "analytics handlers".
|
||||
// scriptURLs: [
|
||||
@@ -540,6 +583,10 @@ var config = {
|
||||
// Decides whether the start/stop recording audio notifications should play on record.
|
||||
// disableRecordAudioNotification: false,
|
||||
|
||||
// Disables the sounds that play when other participants join or leave the
|
||||
// conference (if set to true, these sounds will not be played).
|
||||
// disableJoinLeaveSounds: false,
|
||||
|
||||
// Information for the chrome extension banner
|
||||
// chromeExtensionBanner: {
|
||||
// // The chrome extension to be installed address
|
||||
@@ -599,6 +646,10 @@ var config = {
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false,
|
||||
|
||||
// A property used to unset the default flip state of the local video.
|
||||
// When it is set to 'true', the local(self) video will not be mirrored anymore.
|
||||
// doNotFlipLocalVideo: false,
|
||||
|
||||
// Mainly privacy related settings
|
||||
|
||||
// Disables all invite functions from the app (share, invite, dial out...etc)
|
||||
@@ -620,7 +671,9 @@ var config = {
|
||||
// Options related to the remote participant menu.
|
||||
// remoteVideoMenu: {
|
||||
// // If set to true the 'Kick out' button will be disabled.
|
||||
// disableKick: true
|
||||
// disableKick: true,
|
||||
// // If set to true the 'Grant moderator' button will be disabled.
|
||||
// disableGrantModerator: true
|
||||
// },
|
||||
|
||||
// If set to true all muting operations of remote participants will be disabled.
|
||||
@@ -646,6 +699,9 @@ var config = {
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
// Sets the background transparency level. '0' is fully transparent, '1' is opaque.
|
||||
// backgroundAlpha: 1,
|
||||
|
||||
// The URL of the moderated rooms microservice, if available. If it
|
||||
// is present, a link to the service will be rendered on the welcome page,
|
||||
// otherwise the app doesn't render it.
|
||||
@@ -655,17 +711,22 @@ var config = {
|
||||
// disableTileView: true,
|
||||
|
||||
// Hides the conference subject
|
||||
// hideConferenceSubject: true
|
||||
// hideConferenceSubject: true,
|
||||
|
||||
// Hides the conference timer.
|
||||
// hideConferenceTimer: true,
|
||||
|
||||
// Hides the participants stats
|
||||
// hideParticipantsStats: true
|
||||
// hideParticipantsStats: true,
|
||||
|
||||
// Sets the conference subject
|
||||
// subject: 'Conference Subject',
|
||||
|
||||
// This property is related to the use case when jitsi-meet is used via the IFrame API. When the property is true
|
||||
// jitsi-meet will use the local storage of the host page instead of its own. This option is useful if the browser
|
||||
// is not persisting the local storage inside the iframe.
|
||||
// useHostPageLocalStorage: true,
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
|
||||
@@ -3,18 +3,20 @@
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
|
||||
import { LoginDialog } from './react/features/authentication/components';
|
||||
import { isTokenAuthEnabled } from './react/features/authentication/functions';
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed
|
||||
} from './react/features/base/connection/actions';
|
||||
import { openDialog } from './react/features/base/dialog/actions';
|
||||
import {
|
||||
isFatalJitsiConnectionError,
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
/**
|
||||
@@ -80,7 +82,7 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
* @returns {Promise<JitsiConnection>} connection if
|
||||
* everything is ok, else error.
|
||||
*/
|
||||
function connect(id, password, roomName) {
|
||||
export function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const { jwt } = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
@@ -214,10 +216,38 @@ export function openConnection({ id, password, retry, roomName }) {
|
||||
const { jwt } = APP.store.getState()['features/base/jwt'];
|
||||
|
||||
if (err === JitsiConnectionErrors.PASSWORD_REQUIRED && !jwt) {
|
||||
return AuthHandler.requestAuth(roomName, connect);
|
||||
return requestAuth(roomName);
|
||||
}
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Authentication Dialog and try to connect with new credentials.
|
||||
* If failed to connect because of PASSWORD_REQUIRED error
|
||||
* then ask for password again.
|
||||
* @param {string} [roomName] name of the conference room
|
||||
*
|
||||
* @returns {Promise<JitsiConnection>}
|
||||
*/
|
||||
function requestAuth(roomName) {
|
||||
const config = APP.store.getState()['features/base/config'];
|
||||
|
||||
if (isTokenAuthEnabled(config)) {
|
||||
// This Promise never resolves as user gets redirected to another URL
|
||||
return new Promise(() => redirectToTokenAuthService(roomName));
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
const onSuccess = connection => {
|
||||
resolve(connection);
|
||||
};
|
||||
|
||||
APP.store.dispatch(
|
||||
openDialog(LoginDialog, { onSuccess,
|
||||
roomName })
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,10 +72,20 @@
|
||||
* Keep overflow menu within screen vertical bounds and make it scrollable.
|
||||
*/
|
||||
.toolbox-button-wth-dialog > div:nth-child(2) {
|
||||
background: $menuBG;
|
||||
max-height: calc(100vh - #{$newToolbarSizeWithPadding} - 46px);
|
||||
margin-bottom: 4px;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.audio-preview > div:nth-child(2),
|
||||
.video-preview > div:nth-child(2) {
|
||||
margin-bottom: 4px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following selectors keep the chat modal full-size anywhere between 100px
|
||||
* and 580px for desktop or 680px for mobile.
|
||||
@@ -103,14 +113,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 580px) and (max-width: 680px) {
|
||||
.mobile-browser {
|
||||
&.shift-right .focus-lock > div > div {
|
||||
@include full-size-modal-positioner();
|
||||
}
|
||||
@media (max-width: 580px) {
|
||||
// Override Atlaskit inline style for the modal background.
|
||||
// Important is unfortunately needed for that.
|
||||
.shift-right .focus-lock [role="dialog"][style] {
|
||||
background-color: $chatBackgroundColor !important;
|
||||
}
|
||||
|
||||
&.shift-right .focus-lock [role="dialog"] {
|
||||
@include full-size-modal-dialog();
|
||||
}
|
||||
// Remove Atlaskit padding from the chat dialog.
|
||||
.shift-right .focus-lock [role="dialog"] > div:first-child > div:nth-child(2) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
div.Tooltip {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
.audio-preview {
|
||||
display: inline-block;
|
||||
|
||||
&-content {
|
||||
background: #2A3A4B;
|
||||
font-size: 15px;
|
||||
background: $menuBG;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
max-height: 456px;
|
||||
overflow: auto;
|
||||
width: 328px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
color: #fff;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
margin-top: 8px;
|
||||
padding: 8px 16px;
|
||||
|
||||
&-icon {
|
||||
color: #A4B8D1;
|
||||
display: inline-block;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&--bordered {
|
||||
border-bottom: 1px solid #4C4D50;
|
||||
}
|
||||
|
||||
&-text {
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,19 +40,18 @@
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
padding: 8px 0;
|
||||
margin-left: 48px;
|
||||
|
||||
&--selected {
|
||||
background: #1C2025;
|
||||
background: #131519;
|
||||
cursor: initial;
|
||||
margin-left: 0;
|
||||
padding-left: 21px;
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
display: inline-block;
|
||||
line-height: 24px;
|
||||
text-overflow: ellipsis;
|
||||
@@ -56,12 +66,13 @@
|
||||
|
||||
&:hover {
|
||||
.audio-preview-entry {
|
||||
background: #3F4E5E;
|
||||
background: #36383C;
|
||||
margin-left: 0;
|
||||
padding-left: 48px;
|
||||
|
||||
&--selected {
|
||||
padding-left: 21px;
|
||||
padding-left: 18px;
|
||||
background: $newToolbarBackgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +85,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 256px;
|
||||
}
|
||||
@@ -84,18 +99,19 @@
|
||||
|
||||
&:hover {
|
||||
.audio-preview-entry {
|
||||
background: #3F4E5E;
|
||||
background: #36383C;
|
||||
margin-left: 0;
|
||||
padding-left: 48px;
|
||||
|
||||
&--selected {
|
||||
padding-left: 21px;
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-left: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.audio-preview-entry-text {
|
||||
max-width: 196px;
|
||||
max-width: 178px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +126,7 @@
|
||||
|
||||
&--check {
|
||||
background: #31B76A;
|
||||
margin-right: 13px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&--exclamation {
|
||||
@@ -121,6 +137,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-hr {
|
||||
border-top: 1px solid #4C4D50;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&-test-button {
|
||||
display: none;
|
||||
background: #FFF;
|
||||
@@ -129,23 +150,16 @@
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 4px 16px;
|
||||
padding: 2px 16px;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 8px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
&-meter-mic {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 18px;
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div:nth-child(2) {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
top: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ body {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.disabled .jitsi-icon svg {
|
||||
fill: #929292;
|
||||
}
|
||||
|
||||
.jitsi-icon.gray svg {
|
||||
fill: #5E6D7A;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#sideToolbarContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
background-color: $chatBackgroundColor;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
@@ -105,13 +105,12 @@
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
background-color: $chatHeaderBackgroundColor;
|
||||
height: 70px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
padding: 16px;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
@@ -123,23 +122,27 @@
|
||||
.jitsi-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jitsi-icon > svg {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
padding: 0 16px 24px;
|
||||
padding: 0 16px 16px;
|
||||
|
||||
&.populated {
|
||||
#chat-input {
|
||||
border: 1px solid #619CF4;
|
||||
|
||||
.send-button {
|
||||
background: #1B67EC;
|
||||
cursor: pointer;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: #3D82FB;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #0852D4;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: #fff;
|
||||
}
|
||||
@@ -151,9 +154,13 @@
|
||||
#chat-input {
|
||||
border: 1px solid $chatInputSeparatorColor;
|
||||
display: flex;
|
||||
padding: 5px 10px;
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
|
||||
&:focus-within {
|
||||
border: 1px solid #619CF4;
|
||||
}
|
||||
|
||||
* {
|
||||
background-color: transparent;
|
||||
}
|
||||
@@ -177,10 +184,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser {
|
||||
.send-button {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
.smiley-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#chat-input .smiley-button {
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: #484A4F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +214,7 @@
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
@@ -254,6 +271,14 @@
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.chatmessage .usermessage {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.sideToolbarContainer {
|
||||
@@ -263,8 +288,8 @@
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@@ -292,6 +317,7 @@
|
||||
|
||||
.usermessage {
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.error {
|
||||
@@ -314,12 +340,16 @@
|
||||
}
|
||||
|
||||
.messagecontent {
|
||||
margin: 5px 10px;
|
||||
margin: 8px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.smiley {
|
||||
font-size: 14pt;
|
||||
}
|
||||
@@ -355,7 +385,9 @@
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: $sidebarWidth;
|
||||
width: calc(#{$sidebarWidth} - 32px);
|
||||
margin-bottom: 5px;
|
||||
margin-left: -5px;
|
||||
|
||||
/**
|
||||
* CSS transitions do not apply for auto dimensions. So to produce the css
|
||||
@@ -370,9 +402,8 @@
|
||||
}
|
||||
|
||||
#smileysContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border-bottom: 1px solid;
|
||||
border-top: 1px solid;
|
||||
background-color: $chatBackgroundColor;
|
||||
border-top: 1px solid $chatInputSeparatorColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,9 +509,9 @@
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin: 16px 16px 24px;
|
||||
margin: 16px;
|
||||
width: calc(100% - 32px);
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
@@ -491,19 +522,11 @@
|
||||
.jitsi-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jitsi-icon > svg {
|
||||
fill: #A4B8D1;
|
||||
}
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chat-input-container {
|
||||
padding: 0 0 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.touchmove-hack {
|
||||
@@ -520,6 +543,6 @@
|
||||
place-items: center;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
background: #2a3a4b;
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
}
|
||||
|
||||
.drawer-menu {
|
||||
padding: 0 16px;
|
||||
max-height: 50vh;
|
||||
background: #242528;
|
||||
border-radius: 16px 16px 0 0;
|
||||
@@ -24,12 +23,6 @@
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: $overflowMenuItemHoverBG;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: none;
|
||||
}
|
||||
@@ -67,13 +60,6 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: $overflowMenuItemHoverBG;
|
||||
color: $overflowMenuItemHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
@@ -88,42 +74,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.beta-tag {
|
||||
background: $overflowMenuItemColor;
|
||||
border-radius: 2px;
|
||||
color: $overflowMenuBG;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.overflow-menu-item-icon {
|
||||
margin-right: 16px;
|
||||
|
||||
i {
|
||||
display: inline;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
i:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #B8C7E0 !important;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-text {
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
------------------------------
|
||||
Impromptu
|
||||
------------------------------
|
||||
*/
|
||||
.jqifade{
|
||||
position: absolute;
|
||||
background-color: #000;
|
||||
}
|
||||
div.jqi{
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
color: #3a3a3a;
|
||||
background-color: #ffffff;
|
||||
font-size: 11px;
|
||||
text-align: left;
|
||||
border: solid 1px #eeeeee;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
padding: 7px;
|
||||
}
|
||||
div.jqi .jqicontainer{
|
||||
}
|
||||
div.jqi .jqiclose{
|
||||
position: absolute;
|
||||
top: 4px; right: -2px;
|
||||
width: 18px;
|
||||
cursor: default;
|
||||
color: #bbbbbb;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.jqi .jqistate{
|
||||
background-color: #fff;
|
||||
}
|
||||
div.jqi .jqititle{
|
||||
padding: 5px 10px;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
border-bottom: solid 1px #eeeeee;
|
||||
}
|
||||
div.jqi .jqimessage{
|
||||
padding: 10px;
|
||||
line-height: 20px;
|
||||
color: #444444;
|
||||
}
|
||||
div.jqi .jqibuttons{
|
||||
text-align: right;
|
||||
margin: 0 -7px -7px -7px;
|
||||
border-top: solid 1px #e4e4e4;
|
||||
background-color: #f4f4f4;
|
||||
border-radius: 0 0 6px 6px;
|
||||
-moz-border-radius: 0 0 6px 6px;
|
||||
-webkit-border-radius: 0 0 6px 6px;
|
||||
}
|
||||
div.jqi .jqibuttons button{
|
||||
margin: 0;
|
||||
padding: 5px 20px;
|
||||
background-color: transparent !important;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
border-left: solid 1px #e4e4e4;
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
div.jqi .jqibuttons button.jqidefaultbutton{
|
||||
color: #489afe;
|
||||
}
|
||||
|
||||
div.jqi .jqibuttons button:disabled {
|
||||
color: #b6b6b6 !important;
|
||||
}
|
||||
div.jqi .jqibuttons button:hover,
|
||||
div.jqi .jqibuttons button:focus{
|
||||
color: #287ade;
|
||||
outline: none;
|
||||
}
|
||||
.jqiwarning .jqi .jqibuttons{
|
||||
background-color: #b95656;
|
||||
}
|
||||
|
||||
/* sub states */
|
||||
div.jqi .jqiparentstate::after{
|
||||
background-color: #777;
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top:0;left:0;bottom:0;right:0;
|
||||
border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
}
|
||||
div.jqi .jqisubstate{
|
||||
position: absolute;
|
||||
top:0;
|
||||
left: 20%;
|
||||
width: 60%;
|
||||
padding: 7px;
|
||||
border: solid 1px #eeeeee;
|
||||
border-top: none;
|
||||
border-radius: 0 0 6px 6px;
|
||||
-moz-border-radius: 0 0 6px 6px;
|
||||
-webkit-border-radius: 0 0 6px 6px;
|
||||
}
|
||||
div.jqi .jqisubstate .jqibuttons button{
|
||||
padding: 10px 18px;
|
||||
}
|
||||
|
||||
/* arrows for tooltips/tours */
|
||||
.jqi .jqiarrow{ position: absolute; height: 0; width:0; line-height: 0; font-size: 0; border: solid 10px transparent;}
|
||||
|
||||
.jqi .jqiarrowtl{ left: 10px; top: -20px; border-bottom-color: #ffffff; }
|
||||
.jqi .jqiarrowtc{ left: 50%; top: -20px; border-bottom-color: #ffffff; margin-left: -10px; }
|
||||
.jqi .jqiarrowtr{ right: 10px; top: -20px; border-bottom-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowbl{ left: 10px; bottom: -20px; border-top-color: #ffffff; }
|
||||
.jqi .jqiarrowbc{ left: 50%; bottom: -20px; border-top-color: #ffffff; margin-left: -10px; }
|
||||
.jqi .jqiarrowbr{ right: 10px; bottom: -20px; border-top-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowlt{ left: -20px; top: 10px; border-right-color: #ffffff; }
|
||||
.jqi .jqiarrowlm{ left: -20px; top: 50%; border-right-color: #ffffff; margin-top: -10px; }
|
||||
.jqi .jqiarrowlb{ left: -20px; bottom: 10px; border-right-color: #ffffff; }
|
||||
|
||||
.jqi .jqiarrowrt{ right: -20px; top: 10px; border-left-color: #ffffff; }
|
||||
.jqi .jqiarrowrm{ right: -20px; top: 50%; border-left-color: #ffffff; margin-top: -10px; }
|
||||
.jqi .jqiarrowrb{ right: -20px; bottom: 10px; border-left-color: #ffffff; }
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
@charset "UTF-8";
|
||||
/*!
|
||||
* jQuery contextMenu - Plugin for simple contextMenu handling
|
||||
*
|
||||
* Version: v2.1.1
|
||||
*
|
||||
* Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
|
||||
* Web: http://swisnl.github.io/jQuery-contextMenu/
|
||||
*
|
||||
* Copyright (c) 2011-2016 SWIS BV and contributors
|
||||
*
|
||||
* Licensed under
|
||||
* MIT License http://www.opensource.org/licenses/mit-license
|
||||
*
|
||||
* Date: 2016-02-28T09:53:18.890Z
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "context-menu-icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
src: url("font/context-menu-icons.eot?2qmzf");
|
||||
src: url("font/context-menu-icons.eot?2qmzf#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2qmzf") format("woff2"), url("font/context-menu-icons.woff?2qmzf") format("woff"), url("font/context-menu-icons.ttf?2qmzf") format("truetype");
|
||||
}
|
||||
|
||||
.context-menu-icon:before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 28px;
|
||||
font-family: "context-menu-icons";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #2980b9;
|
||||
text-align: center;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.context-menu-icon-add:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-copy:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-cut:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-delete:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-edit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-paste:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-quit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon.context-menu-hover:before {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-list {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
min-width: 180px;
|
||||
max-width: 360px;
|
||||
padding: 4px 0;
|
||||
margin: 5px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
list-style-type: none;
|
||||
background: #fff;
|
||||
border: 1px solid #bebebe;
|
||||
border-radius: $borderRadius;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
position: relative;
|
||||
padding: 3px 28px;
|
||||
color: #2f2f2f;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
padding: 0;
|
||||
margin: 5px 0;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.context-menu-item > label > input,
|
||||
.context-menu-item > label > textarea {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
}
|
||||
|
||||
.context-menu-input.context-menu-hover,
|
||||
.context-menu-item.context-menu-disabled.context-menu-hover {
|
||||
cursor: default;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.context-menu-submenu:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
z-index: $zindex1;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: '';
|
||||
border-color: transparent transparent transparent #2f2f2f;
|
||||
border-style: solid;
|
||||
border-width: 4px 0 4px 4px;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs
|
||||
*/
|
||||
.context-menu-item.context-menu-input {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* vertically align inside labels */
|
||||
.context-menu-input > label > * {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* position checkboxes and radios as icons */
|
||||
.context-menu-input > label > input[type="checkbox"],
|
||||
.context-menu-input > label > input[type="radio"] {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.context-menu-input > label,
|
||||
.context-menu-input > label > input[type="text"],
|
||||
.context-menu-input > label > textarea,
|
||||
.context-menu-input > label > select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.context-menu-input > label > textarea {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.context-menu-item > .context-menu-list {
|
||||
top: 5px;
|
||||
/* re-positioned by js */
|
||||
right: -5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-visible > .context-menu-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.context-menu-accesskey {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,68 +1,37 @@
|
||||
.large-video-labels {
|
||||
.label {
|
||||
align-items: center;
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
transition: right 0.5s;
|
||||
z-index: $labelsZ;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
height: 28px;
|
||||
margin: 0 0 4px 4px;
|
||||
padding: 0 8px;
|
||||
|
||||
.circular-label {
|
||||
align-items: center;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
opacity: 0.8;
|
||||
&--green {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: #B8C7E0;
|
||||
&--red {
|
||||
background: #E34F56
|
||||
}
|
||||
|
||||
.circular-label.e2ee {
|
||||
align-items: center;
|
||||
background: #76CF9C;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
&--white {
|
||||
background: #fff;
|
||||
color: #5e6d7a;
|
||||
|
||||
.circular-label.file {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.local-rec {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.stream {
|
||||
background: #0065FF;
|
||||
}
|
||||
|
||||
.circular-label.insecure {
|
||||
background: $defaultWarningColor;
|
||||
}
|
||||
|
||||
.recording-label.center-message {
|
||||
background: $videoStateIndicatorBackground;
|
||||
bottom: 50%;
|
||||
display: block;
|
||||
left: 50%;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: $centeredVideoLabelZ;
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.circular-label {
|
||||
background: $videoStateIndicatorBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
height: $videoStateIndicatorSize;
|
||||
line-height: $videoStateIndicatorSize;
|
||||
text-align: center;
|
||||
min-width: $videoStateIndicatorSize;
|
||||
}
|
||||
.label-text-with-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.participants-count {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
.spinner {
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
|
||||
.joining-message {
|
||||
margin: 10px;
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
position: fixed;
|
||||
top: 20;
|
||||
transition: top 1s ease;
|
||||
z-index: 100;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
&.toolbox-visible {
|
||||
// Same as toolbox subject position
|
||||
@@ -62,31 +62,6 @@
|
||||
padding: 15px
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0 15px 15px 15px;
|
||||
|
||||
li {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 8px 0;
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
margin: 0 30px 0 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: unset;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: stretch;
|
||||
margin: 8px 0;
|
||||
@@ -116,3 +91,50 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.knocking-participants-container {
|
||||
list-style-type: none;
|
||||
max-height: 600px;
|
||||
overflow-y: scroll;
|
||||
padding: 0 15px 15px 15px;
|
||||
}
|
||||
|
||||
.knocking-participant {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 8px 0;
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
margin: 0 30px 0 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
align-self: unset;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 300px) {
|
||||
#knocking-participant-list {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
.avatar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.knocking-participant {
|
||||
flex-direction: column;
|
||||
|
||||
.details {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
.toolbox-icon {
|
||||
cursor: pointer;
|
||||
padding: 7px;
|
||||
width: 22px;
|
||||
height : 22px;
|
||||
|
||||
&.toggled {
|
||||
background: $AOTToolbarButtonToggleColor;
|
||||
background: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
@@ -23,44 +25,11 @@
|
||||
flex-direction: row;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.toolbox-button {
|
||||
&:first-child {
|
||||
.toolbox-icon {
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
svg {
|
||||
fill: $hangupColor;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.toolbox-icon {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
padding: 3px !important;
|
||||
}
|
||||
|
||||
.filmstrip-toolbox {
|
||||
flex-direction: column;
|
||||
|
||||
.toolbox-button {
|
||||
&:nth-child(1) {
|
||||
svg {
|
||||
fill: $hangupColor;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
.participants-count {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
color: #5e6d7a;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-left: 16px;
|
||||
padding: 4px 8px;
|
||||
pointer-events: auto;
|
||||
|
||||
&-number {
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background: url('../images/user-groups.svg');
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
51
css/_participants-pane.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
.participants_pane {
|
||||
background-color: $participantsPaneBgColor;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: width .16s ease-in-out;
|
||||
width: 315px;
|
||||
z-index: $zindex0;
|
||||
|
||||
&--closed {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 600;
|
||||
height: 100%;
|
||||
width: 315px;
|
||||
|
||||
& > *:first-child,
|
||||
& > *:last-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
margin: 8px 16px 8px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 375px) {
|
||||
.participants_pane {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
|
||||
&--closed {
|
||||
display: none;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.participants_pane-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
**/
|
||||
|
||||
.popupmenu {
|
||||
min-width: 75px;
|
||||
background-color: $menuBG;
|
||||
border-radius: 3px;
|
||||
min-width: 150px;
|
||||
text-align: left;
|
||||
padding: 0px;
|
||||
white-space: nowrap;
|
||||
@@ -38,6 +40,7 @@
|
||||
|
||||
&__text {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -109,6 +112,6 @@ ul.popupmenu {
|
||||
margin: -16px -24px;
|
||||
}
|
||||
|
||||
span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
|
||||
span.localvideomenu:hover ul.popupmenu, span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
|
||||
display:block !important;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
text {
|
||||
fill: black;
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +197,6 @@
|
||||
&> div {
|
||||
margin: 0 12px;
|
||||
}
|
||||
|
||||
.settings-button-small-icon {
|
||||
right: -8px;
|
||||
|
||||
&--hovered {
|
||||
right: -10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +1,3 @@
|
||||
@mixin small-button-size() {
|
||||
.new-toolbox {
|
||||
.toolbox-content {
|
||||
.button-group-center, .button-group-left, .button-group-right {
|
||||
.toolbox-button {
|
||||
.toolbox-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.toolbox-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin very-small-button-size() {
|
||||
.new-toolbox {
|
||||
.toolbox-content {
|
||||
.button-group-center, .button-group-left, .button-group-right {
|
||||
.settings-button-small-icon {
|
||||
display: none;
|
||||
}
|
||||
.toolbox-button {
|
||||
.toolbox-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.toolbox-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $verySmallScreen) {
|
||||
.welcome {
|
||||
display: block;
|
||||
@@ -122,21 +67,9 @@
|
||||
}
|
||||
|
||||
.desktop-browser {
|
||||
@media only screen and (max-width: $smallScreen) {
|
||||
@include small-button-size();
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $verySmallScreen) {
|
||||
@include very-small-button-size();
|
||||
}
|
||||
|
||||
&.shift-right {
|
||||
@media only screen and (max-width: $smallScreen + $sidebarWidth) {
|
||||
@include small-button-size()
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $verySmallScreen + $sidebarWidth) {
|
||||
@include very-small-button-size();
|
||||
|
||||
#videoResolutionLabel {
|
||||
display: none;
|
||||
|
||||
@@ -1,84 +1,62 @@
|
||||
.settings-button {
|
||||
&-container {
|
||||
position: relative;
|
||||
.settings-button-container {
|
||||
position: relative;
|
||||
|
||||
.toolbox-icon {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d1dbe8;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
.toolbox-icon {
|
||||
align-items: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #929292;
|
||||
background-color: #36383c;
|
||||
|
||||
&:hover {
|
||||
background-color: #daebfa;
|
||||
border: 1px solid #daebfa;
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: #2a3a4b;
|
||||
border: 1px solid #5e6d7a;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #5e6d7a;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
|
||||
&:hover {
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-small-icon {
|
||||
background: #FFF;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
bottom: 0;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.25);
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
right: 4px;
|
||||
width: 16px;
|
||||
|
||||
&> svg {
|
||||
fill: #5e6d7a;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background-color: #a4b8d1;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&--hovered {
|
||||
bottom: -1px;
|
||||
height: 20px;
|
||||
right: 2px;
|
||||
width: 20px;
|
||||
|
||||
&> svg {
|
||||
margin-top: 6px;
|
||||
background-color: #36383c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-button-small-icon {
|
||||
background: #36383C;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
top: -3px;
|
||||
|
||||
&:hover {
|
||||
background: #F2F3F4;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&> svg {
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
&.settings-button-small-icon--disabled {
|
||||
background: #36383C;
|
||||
|
||||
&> svg {
|
||||
fill: #929292;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&> svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background-color: #36383c;
|
||||
cursor: default;
|
||||
|
||||
&> svg {
|
||||
fill: #929292;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,60 @@
|
||||
.subject {
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
margin-top: 20px;
|
||||
position: absolute;
|
||||
top: -120px;
|
||||
transition: top .3s ease-in;
|
||||
height: 95px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
padding: 25px 140px 0 140px;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
color: #fff;
|
||||
z-index: $zindex10;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
z-index: $zindex3;
|
||||
|
||||
&.visible {
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.gradient {
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
|
||||
.subject-info-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-width: calc(100% - 280px);
|
||||
margin: 0 auto;
|
||||
|
||||
&--full-width {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-conference-timer {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
opacity: 0.6;
|
||||
@media (max-width: 500px) {
|
||||
flex-wrap: wrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-bottom: 4px;
|
||||
max-width: 80%;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.subject-text {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
padding: 2px 16px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subject-timer {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
min-width: 34px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
|
||||
&.visible {
|
||||
bottom: 0;
|
||||
.toolbox-background {
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-buttons {
|
||||
@@ -48,253 +45,233 @@
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-background {
|
||||
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
|
||||
transition: bottom .3s ease-in;
|
||||
height: 160px;
|
||||
width: 100%;
|
||||
bottom: -160px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: $toolbarBackgroundZ;
|
||||
.toolbox-content {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
z-index: $toolbarZ;
|
||||
|
||||
.button-group-center,
|
||||
.button-group-left,
|
||||
.button-group-right {
|
||||
display: flex;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.toolbox-content {
|
||||
box-sizing: border-box;
|
||||
.button-group-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.button-group-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.toolbox-button-wth-dialog {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-button {
|
||||
color: $toolbarButtonColor;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
line-height: $newToolbarSize;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbar-button-with-badge {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
.badge-round {
|
||||
bottom: -5px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
min-width: 20px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
background: $newToolbarBackgroundColor;
|
||||
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 6px;
|
||||
margin: 0 auto;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
|
||||
>div {
|
||||
margin-left: 8px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-menu {
|
||||
font-size: 14px;
|
||||
list-style-type: none;
|
||||
padding: 8px 0;
|
||||
background-color: $menuBG;
|
||||
|
||||
.profile-text {
|
||||
max-width: 150px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-menu-item {
|
||||
align-items: center;
|
||||
color: $overflowMenuItemColor;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 40px;
|
||||
line-height: 24px;
|
||||
padding: 8px 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: $overflowMenuItemBackground;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 20px 16px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: $toolbarZ;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button-group-center,
|
||||
.button-group-left,
|
||||
.button-group-right {
|
||||
display: flex;
|
||||
width: 33%;
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
color: #929292;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.button-group-center {
|
||||
justify-content: center;
|
||||
|
||||
.toolbox-button {
|
||||
|
||||
.toolbox-icon {
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #d1dbe8;
|
||||
margin: 0px 4px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: #daebfa;
|
||||
border: 1px solid #daebfa;
|
||||
}
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: #2a3a4b;
|
||||
border: 1px solid #5e6d7a;
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: #5e6d7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled, .disabled & {
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
.toolbox-icon {
|
||||
background-color: $hangupColor;
|
||||
border: 1px solid $hangupColor;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: $hangupColor;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
svg {
|
||||
fill: #929292;
|
||||
}
|
||||
|
||||
.button-group-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&.unclickable:hover {
|
||||
background: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-menu {
|
||||
font-size: 1.2em;
|
||||
list-style-type: none;
|
||||
background-color: $overflowMenuBG;
|
||||
/**
|
||||
* Undo atlaskit padding by reducing margins.
|
||||
*/
|
||||
margin: -16px -24px;
|
||||
padding: 4px 0;
|
||||
.beta-tag {
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
padding: 0 4px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.overflow-menu-item {
|
||||
align-items: center;
|
||||
color: $overflowMenuItemColor;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
padding: 5px 12px;
|
||||
box-sizing: border-box;
|
||||
.overflow-menu-item-icon {
|
||||
margin-right: 16px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
i {
|
||||
display: inline;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: $overflowMenuItemHoverBG;
|
||||
color: $overflowMenuItemHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&.unclickable:hover {
|
||||
background: inherit;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
color: #3b475c;
|
||||
}
|
||||
}
|
||||
|
||||
.beta-tag {
|
||||
background: $overflowMenuItemColor;
|
||||
border-radius: 2px;
|
||||
color: $overflowMenuBG;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.overflow-menu-item-icon {
|
||||
margin-right: 16px;
|
||||
|
||||
i {
|
||||
display: inline;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
i:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #B8C7E0 !important;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-text {
|
||||
max-width: 150px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
i:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-button {
|
||||
color: $toolbarButtonColor;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
line-height: $newToolbarSize;
|
||||
margin: 0 8px;
|
||||
text-align: center;
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.overflow-menu-hr {
|
||||
border-top: 1px solid #4C4D50;
|
||||
border-bottom: 0;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
display: flex;
|
||||
border-radius: 3px;
|
||||
flex-direction: column;
|
||||
font-size: 24px;
|
||||
height: $newToolbarSize;
|
||||
justify-content: center;
|
||||
width: $newToolbarSize;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-button-with-badge {
|
||||
position: relative;
|
||||
@media (max-width: 320px) {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.badge-round {
|
||||
bottom: -5px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
min-width: 20px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
}
|
||||
&.toggled {
|
||||
background: $newToolbarButtonToggleColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: initial !important;
|
||||
background-color: #36383c !important;
|
||||
|
||||
svg {
|
||||
fill: #929292 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-button-wth-dialog {
|
||||
display: inline-block;
|
||||
.hangup-button {
|
||||
background-color: $hangupColor;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background-color: $hangupHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
display: flex;
|
||||
border-radius: 5px;
|
||||
flex-direction: column;
|
||||
font-size: 24px;
|
||||
height: $newToolbarSize;
|
||||
justify-content: center;
|
||||
width: $newToolbarSize;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: initial !important;
|
||||
background-color: #a4b8d1 !important;
|
||||
|
||||
svg {
|
||||
fill: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
svg {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,3 +289,47 @@
|
||||
|
||||
@include transition(all .3s ease-out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio and video buttons do not have toggled state.
|
||||
*/
|
||||
.audio-preview,
|
||||
.video-preview {
|
||||
.toolbox-icon.toggled {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On small mobile devices make the toolbar full width and pad the invite prompt.
|
||||
*/
|
||||
.toolbox-content-mobile {
|
||||
@media (max-width: 500px) {
|
||||
margin-bottom: 0;
|
||||
|
||||
.toolbox-content-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 6px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.invite-more-container {
|
||||
margin: 0 16px 8px;
|
||||
}
|
||||
|
||||
.invite-more-container.elevated {
|
||||
margin-bottom: 52px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
.transcription-subtitles{
|
||||
bottom: 10%;
|
||||
.transcription-subtitles {
|
||||
bottom: $newToolbarSize + 40px;
|
||||
font-size: 16px;
|
||||
font-weight: 1000;
|
||||
left: 50%;
|
||||
max-width: 50vw;
|
||||
opacity: 0.80;
|
||||
overflow-wrap: break-word;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
|
||||
@@ -14,6 +15,11 @@
|
||||
transform: translateX(-50%);
|
||||
z-index: $subtitlesZ;
|
||||
|
||||
&.lifted {
|
||||
// Lift subtitle above toolbar+invite box.
|
||||
bottom: $newToolbarSize + 112px + 40px;
|
||||
}
|
||||
|
||||
span {
|
||||
background: black;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
* Style variables
|
||||
*/
|
||||
$baseFontFamily: -apple-system, BlinkMacSystemFont, 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$hangupColor: #bf2117;
|
||||
$hangupColor:#DD3849;
|
||||
$hangupHoverColor: #F25363;
|
||||
$hangupFontSize: 2em;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,7 @@ $defaultSideBarFontColor: #44A5FF;
|
||||
$defaultSemiDarkColor: #ACACAC;
|
||||
$defaultDarkColor: #2b3d5c;
|
||||
$defaultWarningColor: rgb(215, 121, 118);
|
||||
$participantsPaneBgColor: #141414;
|
||||
$presence-available: rgb(110, 176, 5);
|
||||
$presence-away: rgb(250, 201, 20);
|
||||
$presence-busy: rgb(233, 0, 27);
|
||||
@@ -37,20 +39,18 @@ $presence-idle: rgb(172, 172, 172);
|
||||
/**
|
||||
* Toolbar
|
||||
*/
|
||||
$newToolbarBackgroundColor: rgba(22, 38, 55, 0.8);
|
||||
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.15);
|
||||
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.2);
|
||||
$AOTToolbarButtonHoverColor: rgba(14, 20, 35, 0.6);
|
||||
$AOTToolbarButtonToggleColor: rgba(14, 20, 35, 1);
|
||||
$newToolbarBackgroundColor: #131519;
|
||||
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.2);
|
||||
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.15);
|
||||
$menuBG:#242528;
|
||||
$newToolbarFontSize: 24px;
|
||||
$newToolbarHangupFontSize: 32px;
|
||||
$newToolbarSize: 40px;
|
||||
$newToolbarSize: 48px;
|
||||
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
|
||||
$toolbarTitleFontSize: 19px;
|
||||
$overflowMenuBG: initial;
|
||||
$overflowMenuItemHoverBG: #313D52;
|
||||
$overflowMenuItemHoverColor: #B8C7E0;
|
||||
$overflowMenuItemColor: #B8C7E0;
|
||||
$overflowMenuItemColor: #fff;
|
||||
$overflowMenuItemBackground: #36383C;
|
||||
|
||||
|
||||
/**
|
||||
* Video layout
|
||||
@@ -90,12 +90,12 @@ $modalTextColor: #333;
|
||||
* Chat
|
||||
*/
|
||||
$chatActionsSeparatorColor: rgb(173, 105, 112);
|
||||
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
|
||||
$chatBackgroundColor: #131519;
|
||||
$chatInputSeparatorColor: #A4B8D1;
|
||||
$chatLocalMessageBackgroundColor: rgb(4, 98, 178);
|
||||
$chatLocalMessageBackgroundColor: #484A4F;
|
||||
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
|
||||
$chatRemoteMessageBackgroundColor: rgb(86, 101, 114);
|
||||
$sidebarWidth: 375px;
|
||||
$chatRemoteMessageBackgroundColor: #242528;
|
||||
$sidebarWidth: 315px;
|
||||
|
||||
/**
|
||||
* Misc.
|
||||
@@ -122,8 +122,8 @@ $zindex10: 10;
|
||||
$reloadZ: 20;
|
||||
$poweredByZ: 100;
|
||||
$ringingZ: 300;
|
||||
$sideToolbarContainerZ: 300;
|
||||
$toolbarZ: 350;
|
||||
$sideToolbarContainerZ: 200;
|
||||
$toolbarZ: 250;
|
||||
$drawerZ: 351;
|
||||
$tooltipsZ: 401;
|
||||
$dropdownMaskZ: 900;
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
.video-preview {
|
||||
background: none;
|
||||
max-height: 290px;
|
||||
display: inline-block;
|
||||
max-height: 344px;
|
||||
|
||||
&-container {
|
||||
background: $menuBG;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
&-entry {
|
||||
cursor: pointer;
|
||||
height: 135px;
|
||||
margin-bottom: 16px;
|
||||
height: 168px;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
width: 240px;
|
||||
width: 284px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@@ -20,13 +23,15 @@
|
||||
|
||||
&--selected {
|
||||
border: 3px solid #31B76A;
|
||||
border-radius: 3px;
|
||||
cursor: default;
|
||||
height: 129px;
|
||||
width: 234px;
|
||||
height: 162px;
|
||||
width: 278px;
|
||||
}
|
||||
}
|
||||
|
||||
&-video {
|
||||
border-radius: 3px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
@@ -50,21 +55,28 @@
|
||||
}
|
||||
|
||||
&-label {
|
||||
bottom: 8px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
width: 220px;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
// Override @atlaskit/InlineDialog container which is made with styled components
|
||||
& > div:nth-child(2) {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
&-container {
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
background-color: #131519;
|
||||
border-radius: 3px;
|
||||
padding: 2px 8px;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin: 0 auto;
|
||||
max-width: calc(100% - 16px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#videoconference_page {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
transform: translate3d(0, 0, 0);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#layout_wrapper {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#videospace {
|
||||
@@ -400,7 +408,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger,
|
||||
.localvideomenu,
|
||||
.remotevideomenu
|
||||
{
|
||||
display: inline-block;
|
||||
@@ -418,6 +428,7 @@
|
||||
cursor: hand;
|
||||
}
|
||||
}
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
@@ -208,6 +208,11 @@ body.welcome-page {
|
||||
cursor: pointer;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-watermark {
|
||||
@@ -228,6 +233,10 @@ body.welcome-page {
|
||||
}
|
||||
}
|
||||
|
||||
&.without-footer {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.welcome-cards-container {
|
||||
color:#131519;
|
||||
padding-top: 40px;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
$rangeInputThumbSize: 14;
|
||||
|
||||
/**
|
||||
* Disable the default webkit styles for range inputs (sliders).
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
0 0 3px $videoThumbnailSelected;
|
||||
}
|
||||
|
||||
.remotevideomenu > .icon-menu {
|
||||
.remotevideomenu > .icon-menu, .localvideomenu > .icon-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
box-shadow: inset 0 0 3px $videoThumbnailHovered,
|
||||
0 0 3px $videoThumbnailHovered;
|
||||
|
||||
.remotevideomenu > .icon-menu {
|
||||
.remotevideomenu > .icon-menu, .localvideomenu > .icon-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filmstrip__videos .videocontainer {
|
||||
@@ -50,10 +50,6 @@
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
width: calc(100vw - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,3 +117,15 @@
|
||||
align-self: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.shift-right #filmstripRemoteVideosContainer {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
* specifically the various status icons.
|
||||
*/
|
||||
.remotevideomenu,
|
||||
.localvideomenu,
|
||||
.videocontainer__toptoolbar {
|
||||
z-index: auto;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
* and tooltips from getting a new location context due to translate3d.
|
||||
*/
|
||||
.connection-indicator,
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger,
|
||||
.indicator-icon-container {
|
||||
transform: translate3d(0, 0, 0);
|
||||
@@ -68,7 +69,9 @@
|
||||
* Move the remote video menu trigger to the bottom left of the video
|
||||
* thumbnail.
|
||||
*/
|
||||
.localvideomenu,
|
||||
.remotevideomenu,
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@@ -76,6 +79,7 @@
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.local-video-menu-trigger,
|
||||
.remote-video-menu-trigger {
|
||||
margin-bottom: 7px;
|
||||
margin-left: $remoteVideoMenuIconMargin;
|
||||
|
||||
@@ -43,11 +43,11 @@ $flagsImagePath: "../images/";
|
||||
@import 'modals/settings/settings';
|
||||
@import 'modals/speaker_stats/speaker_stats';
|
||||
@import 'modals/video-quality/video-quality';
|
||||
@import 'modals/virtual-background/virtual-background';
|
||||
@import 'modals/local-recording/local-recording';
|
||||
@import 'videolayout_default';
|
||||
@import 'notice';
|
||||
@import 'subject';
|
||||
@import 'participants-count';
|
||||
@import 'popup_menu';
|
||||
@import 'recording';
|
||||
@import 'login_menu';
|
||||
@@ -57,7 +57,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'welcome_page_content';
|
||||
@import 'welcome_page_settings_toolbar';
|
||||
@import 'toolbars';
|
||||
@import 'jquery.contextMenu';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'redirect_page';
|
||||
@import 'components/form-control';
|
||||
@@ -104,5 +103,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'responsive';
|
||||
@import 'connection-status';
|
||||
@import 'drawer';
|
||||
@import 'participants-pane';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -1,31 +1,42 @@
|
||||
.invite-more {
|
||||
&-container {
|
||||
margin-bottom: 8px;
|
||||
transition: margin-bottom 0.3s;
|
||||
|
||||
&.elevated {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 600;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
z-index: $zindex2;
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
&-header {
|
||||
font-size: 19px;
|
||||
line-height: 28px;
|
||||
margin: 24px 0 16px 0;
|
||||
max-width: 100%;
|
||||
margin-bottom: 16px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
max-width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 16px;
|
||||
width: fit-content;
|
||||
width: -moz-fit-content;
|
||||
height: 24px;
|
||||
background: #0376DA;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
@@ -36,8 +47,9 @@
|
||||
|
||||
&-text {
|
||||
margin-left: 8px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
&-dialog {
|
||||
@@ -85,18 +97,18 @@
|
||||
border-top: none;
|
||||
border-radius: 0 0 3px 3px;
|
||||
|
||||
& > * {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.copy-invite-icon, .provider-icon {
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
place-content: center;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:hover > div:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
& > :not(:last-child) {
|
||||
@@ -195,3 +207,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser {
|
||||
.invite-more-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.invite-more-button {
|
||||
height: 48px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
|
||||
.language-settings {
|
||||
max-width: 50%;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.calendar-tab {
|
||||
|
||||
159
css/modals/virtual-background/_virtual-background.scss
Normal file
@@ -0,0 +1,159 @@
|
||||
.virtual-background-dialog {
|
||||
max-height: 300px;
|
||||
color: white;
|
||||
display: inline-grid;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
column-gap: 8px;
|
||||
cursor: pointer;
|
||||
.thumbnail:hover, .blur:hover, .slight-blur:hover, .virtual-background-none:hover{
|
||||
height: 56px;
|
||||
width: 103px;
|
||||
opacity: .5;
|
||||
border: 2px solid #99bbf3;
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
}
|
||||
}
|
||||
.thumbnail {
|
||||
margin-top: 8px;
|
||||
border-radius: 6px;
|
||||
object-fit: cover;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
}
|
||||
|
||||
.thumbnail:hover ~ .delete-image-icon {
|
||||
display: block;
|
||||
}
|
||||
.thumbnail-selected {
|
||||
margin-top: 8px;
|
||||
border-radius: 6px;
|
||||
object-fit: cover;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border: 2px solid #246FE5;
|
||||
}
|
||||
.blur{
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
margin-top: 8px;
|
||||
background: #7E8287;
|
||||
font-weight: bold;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 60px;
|
||||
}
|
||||
.blur-selected {
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
margin-top: 8px;
|
||||
background: #7E8287;
|
||||
font-weight: bold;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #246FE5;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 60px;
|
||||
}
|
||||
.slight-blur{
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
margin-top: 8px;
|
||||
background: #A4A4A4;
|
||||
font-weight: bold;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 60px;
|
||||
}
|
||||
.slight-blur-selected{
|
||||
box-shadow: inset 0 0 12px #000000;
|
||||
margin-top: 8px;
|
||||
background: #A4A4A4;
|
||||
font-weight: bold;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #246FE5;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 60px;
|
||||
}
|
||||
.virtual-background-none {
|
||||
margin-top: 8px;
|
||||
background: #525252;
|
||||
font-weight: bold;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 60px;
|
||||
}
|
||||
.none-selected {
|
||||
margin-top: 8px;
|
||||
background: #525252;
|
||||
font-weight: bold;
|
||||
height: 60px;
|
||||
width: 107px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #246FE5;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 60px;
|
||||
}
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
font-size: 1.5vw;
|
||||
.virtual-background-none, .thumbnail, .thumbnail-selected, .none-selected, .blur, .blur-selected, .slight-blur, .slight-blur-selected{
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form .virtual-background-loading {
|
||||
overflow: hidden;
|
||||
}
|
||||
.file-upload-btn {
|
||||
display: none;
|
||||
}
|
||||
.file-upload-label{
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
margin-bottom: 8px;
|
||||
color: #669AEC;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete-image-icon {
|
||||
background: #3d3d3d;
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 96;
|
||||
bottom: 51;
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
left: 51px
|
||||
}
|
||||
}
|
||||
.delete-image-icon:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumbnail-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-content-text{
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.add-background{
|
||||
margin-right: 8px;
|
||||
}
|
||||
4
debian/jitsi-meet-prosody.postinst
vendored
@@ -35,7 +35,7 @@ case "$1" in
|
||||
db_input critical jitsi-videobridge/jvb-hostname || true
|
||||
db_go
|
||||
fi
|
||||
JVB_HOSTNAME="$RET"
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
|
||||
db_get jitsi-videobridge/jvbsecret
|
||||
if [ -z "$RET" ] ; then
|
||||
@@ -64,7 +64,7 @@ case "$1" in
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet-prosody/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
JVB_HOSTNAME_OLD=$(echo "$RET" | xargs echo -n)
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME_OLD.cfg.lua
|
||||
rm -f /etc/prosody/certs/$JVB_HOSTNAME_OLD.key
|
||||
|
||||
2
debian/jitsi-meet-prosody.postrm
vendored
@@ -31,7 +31,7 @@ case "$1" in
|
||||
|
||||
purge)
|
||||
db_get jitsi-meet-prosody/jvb-hostname
|
||||
JVB_HOSTNAME=$RET
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
if [ -n "$RET" ]; then
|
||||
rm -f /etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua
|
||||
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
|
||||
|
||||
2
debian/jitsi-meet-tokens.postinst
vendored
@@ -25,7 +25,7 @@ case "$1" in
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
db_get jitsi-meet-prosody/jvb-hostname
|
||||
JVB_HOSTNAME="$RET"
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
|
||||
db_get jitsi-meet-tokens/appid
|
||||
if [ "$RET" = "false" ] ; then
|
||||
|
||||
4
debian/jitsi-meet-turnserver.postinst
vendored
@@ -30,7 +30,7 @@ case "$1" in
|
||||
db_input critical jitsi-videobridge/jvb-hostname || true
|
||||
db_go
|
||||
fi
|
||||
JVB_HOSTNAME="$RET"
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
|
||||
TURN_CONFIG="/etc/turnserver.conf"
|
||||
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
|
||||
@@ -44,7 +44,7 @@ case "$1" in
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet-turnserver/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
JVB_HOSTNAME_OLD=$(echo "$RET" | xargs echo -n)
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
if [[ -f $TURN_CONFIG ]] && grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
|
||||
rm -f $TURN_CONFIG
|
||||
|
||||
4
debian/jitsi-meet-web-config.postinst
vendored
@@ -32,12 +32,12 @@ case "$1" in
|
||||
db_go
|
||||
db_get jitsi-videobridge/jvb-hostname
|
||||
fi
|
||||
JVB_HOSTNAME="$RET"
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
|
||||
# detect dpkg-reconfigure
|
||||
RECONFIGURING="false"
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
JVB_HOSTNAME_OLD=$(echo "$RET" | xargs echo -n)
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
RECONFIGURING="true"
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js
|
||||
|
||||
2
debian/jitsi-meet-web-config.postrm
vendored
@@ -33,7 +33,7 @@ case "$1" in
|
||||
;;
|
||||
purge)
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
JVB_HOSTNAME=$RET
|
||||
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
|
||||
if [ -n "$RET" ]; then
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME-config.js
|
||||
rm -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
|
||||
1
debian/jitsi-meet-web.install
vendored
@@ -1,6 +1,5 @@
|
||||
interface_config.js /usr/share/jitsi-meet/
|
||||
logging_config.js /usr/share/jitsi-meet/
|
||||
*.json /usr/share/jitsi-meet/
|
||||
*.html /usr/share/jitsi-meet/
|
||||
*.ico /usr/share/jitsi-meet/
|
||||
libs /usr/share/jitsi-meet/
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName jitsi-meet.example.com
|
||||
Redirect permanent / https://jitsi-meet.example.com/
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
@@ -42,11 +39,12 @@
|
||||
</Location>
|
||||
|
||||
ProxyPreserveHost on
|
||||
ProxyPass /http-bind http://localhost:5280/http-bind/
|
||||
ProxyPassReverse /http-bind http://localhost:5280/http-bind/
|
||||
ProxyPass /http-bind http://localhost:5280/http-bind
|
||||
ProxyPassReverse /http-bind http://localhost:5280/http-bind
|
||||
ProxyPass /xmpp-websocket ws://localhost:5280/xmpp-websocket
|
||||
ProxyPassReverse /xmpp-websocket ws://localhost:5280/xmpp-websocket
|
||||
ProxyPassMatch ^/colibri-ws/default-id ws://localhost:9090
|
||||
ProxyPass /colibri-ws/default-id ws://localhost:9090/colibri-ws/default-id
|
||||
ProxyPassReverse /colibri-ws/default-id ws://localhost:9090/colibri-ws/default-id
|
||||
|
||||
RewriteEngine on
|
||||
RewriteRule ^/([a-zA-Z0-9]+)$ /index.html
|
||||
|
||||
BIN
images/virtual-background/background-1.jpg
Normal file
|
After Width: | Height: | Size: 463 KiB |
BIN
images/virtual-background/background-2.jpg
Normal file
|
After Width: | Height: | Size: 338 KiB |
BIN
images/virtual-background/background-3.jpg
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
images/virtual-background/background-4.jpg
Normal file
|
After Width: | Height: | Size: 685 KiB |
BIN
images/virtual-background/background-5.jpg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
images/virtual-background/background-6.jpg
Normal file
|
After Width: | Height: | Size: 599 KiB |
BIN
images/virtual-background/background-7.jpg
Normal file
|
After Width: | Height: | Size: 894 KiB |
@@ -1,6 +1,13 @@
|
||||
/* eslint-disable no-unused-vars, no-var, max-len */
|
||||
/* eslint sort-keys: ["error", "asc", {"caseSensitive": false}] */
|
||||
|
||||
/**
|
||||
* !!!IMPORTANT!!!
|
||||
*
|
||||
* This file is considered deprecated. All options will eventually be moved to
|
||||
* config.js, and no new options should be added here.
|
||||
*/
|
||||
|
||||
var interfaceConfig = {
|
||||
APP_NAME: 'Jitsi Meet',
|
||||
AUDIO_LEVEL_PRIMARY_COLOR: 'rgba(255,255,255,0.4)',
|
||||
@@ -198,23 +205,16 @@ var interfaceConfig = {
|
||||
TOOLBAR_ALWAYS_VISIBLE: false,
|
||||
|
||||
/**
|
||||
* The name of the toolbar buttons to display in the toolbar, including the
|
||||
* "More actions" menu. If present, the button will display. Exceptions are
|
||||
* "livestreaming" and "recording" which also require being a moderator and
|
||||
* some values in config.js to be enabled. Also, the "profile" button will
|
||||
* not display for users with a JWT.
|
||||
* Notes:
|
||||
* - 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
|
||||
* DEPRECATED!
|
||||
* This config was moved to config.js as `toolbarButtons`.
|
||||
*/
|
||||
TOOLBAR_BUTTONS: [
|
||||
'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
],
|
||||
// TOOLBAR_BUTTONS: [
|
||||
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
// ],
|
||||
|
||||
TOOLBAR_TIMEOUT: 4000,
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ target 'JitsiMeetSDK' do
|
||||
pod 'react-native-splash-screen', :path => '../node_modules/react-native-splash-screen'
|
||||
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'
|
||||
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-async-storage/async-storage'
|
||||
pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'
|
||||
pod 'RNGoogleSignin', :path => '../node_modules/@react-native-community/google-signin'
|
||||
|
||||
@@ -5,8 +5,6 @@ PODS:
|
||||
- AppAuth/Core (1.2.0)
|
||||
- AppAuth/ExternalUserAgent (1.2.0)
|
||||
- boost-for-react-native (1.63.0)
|
||||
- BVLinearGradient (2.5.6):
|
||||
- React
|
||||
- CocoaLumberjack (3.5.3):
|
||||
- CocoaLumberjack/Core (= 3.5.3)
|
||||
- CocoaLumberjack/Core (3.5.3)
|
||||
@@ -292,7 +290,7 @@ PODS:
|
||||
- React
|
||||
- react-native-splash-screen (3.2.0):
|
||||
- React
|
||||
- react-native-webrtc (1.87.3):
|
||||
- react-native-webrtc (1.89.1):
|
||||
- React-Core
|
||||
- react-native-webview (11.0.2):
|
||||
- React-Core
|
||||
@@ -371,7 +369,6 @@ PODS:
|
||||
- Yoga (1.14.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
|
||||
- CocoaLumberjack (~> 3.5.3)
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector/`)
|
||||
@@ -442,8 +439,6 @@ SPEC REPOS:
|
||||
- PromisesObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
BVLinearGradient:
|
||||
:path: "../node_modules/react-native-linear-gradient"
|
||||
DoubleConversion:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
FBLazyVector:
|
||||
@@ -526,7 +521,6 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: bce82c76043657c99d91e7882e8a9e1a93650cd4
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
|
||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||
FBLazyVector: ca7f56c8ff6cd8590f7a673d7903b06019805581
|
||||
@@ -563,7 +557,7 @@ SPEC CHECKSUMS:
|
||||
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
|
||||
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
|
||||
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
|
||||
react-native-webrtc: dc1208bdca2c4d091f7b57859e69332bff6f1986
|
||||
react-native-webrtc: ccb0c21eb4fb04326648fbdb4a5d49977e2cf274
|
||||
react-native-webview: b2542d6fd424bcc3e3b2ec5f854f0abb4ec86c87
|
||||
React-RCTActionSheet: bcbc311dc3b47bc8efb2737ff0940239a45789a9
|
||||
React-RCTAnimation: 65f61080ce632f6dea23d52e354ffac9948396c6
|
||||
@@ -584,6 +578,6 @@ SPEC CHECKSUMS:
|
||||
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
|
||||
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
|
||||
|
||||
PODFILE CHECKSUM: 5be5132e41831a98362eeed760558227a4df89ae
|
||||
PODFILE CHECKSUM: d059cebf82da14a53940a16c24c3330752d4b0c8
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
<string>applinks:beta.meet.jit.si</string>
|
||||
<string>applinks:meet.jit.si</string>
|
||||
</array>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.jitsi.meet.appgroup</string>
|
||||
</array>
|
||||
<key>com.apple.developer.siri</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
@@ -23,6 +23,13 @@
|
||||
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 */; };
|
||||
4E90F9402632D1AB001102D4 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E90F93F2632D1AB001102D4 /* Atomic.swift */; };
|
||||
4EB06024260E026600F524C5 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC49B8625BED71300E76218 /* ReplayKit.framework */; };
|
||||
4EB06027260E026600F524C5 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06026260E026600F524C5 /* SampleHandler.swift */; };
|
||||
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
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 */; };
|
||||
55BEDABDA92D47D399A70A5E /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D878B07B3FBD6E305EAA6B27 /* libPods-JitsiMeet.a */; };
|
||||
DE050389256E904600DEE3A5 /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE050388256E904600DEE3A5 /* WebRTC.xcframework */; };
|
||||
DE05038A256E904600DEE3A5 /* WebRTC.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE050388256E904600DEE3A5 /* WebRTC.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -48,6 +55,13 @@
|
||||
remoteGlobalIDString = 0BEA5C241F7B8F73000D0AB4;
|
||||
remoteInfo = JitsiMeetCompanion;
|
||||
};
|
||||
4EB06029260E026600F524C5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 4EB06022260E026600F524C5;
|
||||
remoteInfo = "JitsiMeetBroadcast Extension";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -85,6 +99,17 @@
|
||||
name = "Embed Watch Content";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EC49B9025BED71300E76218 /* Embed App Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -115,7 +140,18 @@
|
||||
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>"; };
|
||||
4670A512A688E2DC34528282 /* Pods-jitsi-meet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-jitsi-meet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-jitsi-meet/Pods-jitsi-meet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
4E90F93F2632D1AB001102D4 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
|
||||
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = JitsiMeetBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4EB06026260E026600F524C5 /* SampleHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleHandler.swift; sourceTree = "<group>"; };
|
||||
4EB06028260E026600F524C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
4EB06032260E08CC00F524C5 /* extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = extension.entitlements; sourceTree = "<group>"; };
|
||||
4EB06039260E09D000F524C5 /* SocketConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketConnection.swift; sourceTree = "<group>"; };
|
||||
4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinNotificationCenter.swift; sourceTree = "<group>"; };
|
||||
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; };
|
||||
5FEF9D87A4D2A38AD7193308 /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet-JitsiMeetBroadcastExtension/Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||
609CB2080B75F75A89923F3D /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A7B2827E068A0E05260054AC /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet-JitsiMeetBroadcastExtension/Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
|
||||
D878B07B3FBD6E305EAA6B27 /* 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>"; };
|
||||
@@ -153,6 +189,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EB06020260E026600F524C5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4EB06024260E026600F524C5 /* ReplayKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -165,6 +209,7 @@
|
||||
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */,
|
||||
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */,
|
||||
D878B07B3FBD6E305EAA6B27 /* libPods-JitsiMeet.a */,
|
||||
4EC49B8625BED71300E76218 /* ReplayKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -216,6 +261,23 @@
|
||||
path = src;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EB06032260E08CC00F524C5 /* extension.entitlements */,
|
||||
4EB06026260E026600F524C5 /* SampleHandler.swift */,
|
||||
4EB0603B260E09D000F524C5 /* SampleUploader.swift */,
|
||||
4EB06039260E09D000F524C5 /* SocketConnection.swift */,
|
||||
4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */,
|
||||
4E90F93F2632D1AB001102D4 /* Atomic.swift */,
|
||||
4EB06028260E026600F524C5 /* Info.plist */,
|
||||
);
|
||||
indentWidth = 4;
|
||||
name = "JitsiMeetBroadcast Extension";
|
||||
path = "broadcast-extension";
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 4;
|
||||
};
|
||||
5E96ADD5E49F3B3822EF9A52 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -223,6 +285,8 @@
|
||||
09AA3B93E4CC62D84B424690 /* Pods-jitsi-meet.release.xcconfig */,
|
||||
609CB2080B75F75A89923F3D /* Pods-JitsiMeet.debug.xcconfig */,
|
||||
FC040BBED70876444D89E91C /* Pods-JitsiMeet.release.xcconfig */,
|
||||
A7B2827E068A0E05260054AC /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig */,
|
||||
5FEF9D87A4D2A38AD7193308 /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
@@ -237,6 +301,7 @@
|
||||
5E96ADD5E49F3B3822EF9A52 /* Pods */,
|
||||
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
|
||||
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
|
||||
4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
@@ -248,6 +313,7 @@
|
||||
13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
|
||||
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
|
||||
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
|
||||
4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -305,23 +371,43 @@
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
|
||||
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
|
||||
4EC49B9025BED71300E76218 /* Embed App Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
|
||||
4EB0602A260E026600F524C5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = JitsiMeet;
|
||||
productName = "Jitsi Meet";
|
||||
productReference = 13B07F961A680F5B00A75B9A /* jitsi-meet.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 4EB0602C260E026700F524C5 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcastExtension" */;
|
||||
buildPhases = (
|
||||
4EB0601F260E026600F524C5 /* Sources */,
|
||||
4EB06020260E026600F524C5 /* Frameworks */,
|
||||
4EB06021260E026600F524C5 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = JitsiMeetBroadcastExtension;
|
||||
productName = "JitsiMeetBroadcast Extension";
|
||||
productReference = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1240;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
@@ -336,8 +422,6 @@
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
DevelopmentTeam = FC967L3QRG;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.SafariKeychain = {
|
||||
enabled = 1;
|
||||
@@ -347,6 +431,9 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
4EB06022260E026600F524C5 = {
|
||||
CreatedOnToolsVersion = 12.4;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
|
||||
@@ -365,6 +452,7 @@
|
||||
13B07F861A680F5B00A75B9A /* JitsiMeet */,
|
||||
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
|
||||
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
|
||||
4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -397,6 +485,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EB06021260E026600F524C5 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
@@ -532,6 +627,18 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EB0601F260E026600F524C5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4EB0603C260E09D000F524C5 /* SocketConnection.swift in Sources */,
|
||||
4EB0603E260E09D000F524C5 /* SampleUploader.swift in Sources */,
|
||||
4EB0603D260E09D000F524C5 /* DarwinNotificationCenter.swift in Sources */,
|
||||
4EB06027260E026600F524C5 /* SampleHandler.swift in Sources */,
|
||||
4E90F9402632D1AB001102D4 /* Atomic.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -545,6 +652,11 @@
|
||||
target = 0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */;
|
||||
targetProxy = 0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */;
|
||||
};
|
||||
4EB0602A260E026600F524C5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */;
|
||||
targetProxy = 4EB06029260E026600F524C5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -718,7 +830,7 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
|
||||
CODE_SIGN_ENTITLEMENTS = app.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
@@ -748,7 +860,7 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
|
||||
CODE_SIGN_ENTITLEMENTS = app.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
@@ -770,6 +882,75 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
4EB0602D260E026700F524C5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "broadcast-extension/extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "broadcast-extension/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.broadcast.extension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4EB0602E260E026700F524C5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "broadcast-extension/extension.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = "broadcast-extension/Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.broadcast.extension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -914,6 +1095,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
4EB0602C260E026700F524C5 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcastExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4EB0602D260E026700F524C5 /* Debug */,
|
||||
4EB0602E260E026700F524C5 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
30
ios/app/broadcast-extension/Atomic.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
@propertyWrapper
|
||||
struct Atomic<Value> {
|
||||
|
||||
private var value: Value
|
||||
private let lock = NSLock()
|
||||
|
||||
init(wrappedValue value: Value) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
var wrappedValue: Value {
|
||||
get { return load() }
|
||||
set { store(newValue: newValue) }
|
||||
}
|
||||
|
||||
func load() -> Value {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
return value
|
||||
}
|
||||
|
||||
mutating func store(newValue: Value) {
|
||||
lock.lock()
|
||||
defer { lock.unlock() }
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
37
ios/app/broadcast-extension/DarwinNotificationCenter.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright @ 2021-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 Foundation
|
||||
|
||||
enum DarwinNotification: String {
|
||||
case broadcastStarted = "iOS_BroadcastStarted"
|
||||
case broadcastStopped = "iOS_BroadcastStopped"
|
||||
}
|
||||
|
||||
class DarwinNotificationCenter {
|
||||
|
||||
static var shared = DarwinNotificationCenter()
|
||||
|
||||
private var notificationCenter: CFNotificationCenter
|
||||
|
||||
init() {
|
||||
notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
}
|
||||
|
||||
func postNotification(_ name: DarwinNotification) {
|
||||
CFNotificationCenterPostNotification(notificationCenter, CFNotificationName(rawValue: name.rawValue as CFString), nil, nil, true)
|
||||
}
|
||||
}
|
||||
33
ios/app/broadcast-extension/Info.plist
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Jitsi Meet Broadcast Extension</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.broadcast-services-upload</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SampleHandler</string>
|
||||
<key>RPBroadcastProcessMode</key>
|
||||
<string>RPBroadcastProcessModeSampleBuffer</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
117
ios/app/broadcast-extension/SampleHandler.swift
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright @ 2021-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 ReplayKit
|
||||
import JitsiMeetSDK
|
||||
|
||||
private enum Constants {
|
||||
// the App Group ID value that the app and the broadcast extension targets are setup with. It differs for each app.
|
||||
static let appGroupIdentifier = "group.org.jitsi.meet.appgroup"
|
||||
}
|
||||
|
||||
class SampleHandler: RPBroadcastSampleHandler {
|
||||
|
||||
private var clientConnection: SocketConnection?
|
||||
private var uploader: SampleUploader?
|
||||
|
||||
private var frameCount: Int = 0
|
||||
|
||||
var socketFilePath: String {
|
||||
let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
|
||||
|
||||
return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
if let connection = SocketConnection(filePath: socketFilePath) {
|
||||
clientConnection = connection
|
||||
setupConnection()
|
||||
|
||||
uploader = SampleUploader(connection: connection)
|
||||
}
|
||||
}
|
||||
|
||||
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
|
||||
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
|
||||
print("broadcast started")
|
||||
|
||||
frameCount = 0
|
||||
|
||||
DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
|
||||
openConnection()
|
||||
}
|
||||
|
||||
override func broadcastPaused() {
|
||||
// User has requested to pause the broadcast. Samples will stop being delivered.
|
||||
}
|
||||
|
||||
override func broadcastResumed() {
|
||||
// User has requested to resume the broadcast. Samples delivery will resume.
|
||||
}
|
||||
|
||||
override func broadcastFinished() {
|
||||
// User has requested to finish the broadcast.
|
||||
DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
|
||||
clientConnection?.close()
|
||||
}
|
||||
|
||||
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
|
||||
switch sampleBufferType {
|
||||
case RPSampleBufferType.video:
|
||||
// very simple mechanism for adjusting frame rate by using every third frame
|
||||
frameCount += 1
|
||||
if frameCount % 3 == 0 {
|
||||
uploader?.send(sample: sampleBuffer)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SampleHandler {
|
||||
|
||||
func setupConnection() {
|
||||
clientConnection?.didClose = { [weak self] error in
|
||||
print("client connection did close \(String(describing: error))")
|
||||
|
||||
if let error = error {
|
||||
self?.finishBroadcastWithError(error)
|
||||
} else {
|
||||
// the displayed failure message is more user friendly when using NSError instead of Error
|
||||
let JMScreenSharingStopped = 10001
|
||||
let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
|
||||
self?.finishBroadcastWithError(customError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openConnection() {
|
||||
let queue = DispatchQueue(label: "broadcast.connectTimer")
|
||||
let timer = DispatchSource.makeTimerSource(queue: queue)
|
||||
timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
|
||||
timer.setEventHandler { [weak self] in
|
||||
guard self?.clientConnection?.open() == true else {
|
||||
return
|
||||
}
|
||||
|
||||
timer.cancel()
|
||||
}
|
||||
|
||||
timer.resume()
|
||||
}
|
||||
}
|
||||
154
ios/app/broadcast-extension/SampleUploader.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright @ 2021-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 Foundation
|
||||
import ReplayKit
|
||||
|
||||
private enum Constants {
|
||||
static let bufferMaxLength = 10240
|
||||
}
|
||||
|
||||
class SampleUploader {
|
||||
|
||||
private static var imageContext = CIContext(options: nil)
|
||||
|
||||
@Atomic private var isReady: Bool = false
|
||||
private var connection: SocketConnection
|
||||
|
||||
private var dataToSend: Data?
|
||||
private var byteIndex = 0
|
||||
|
||||
private let serialQueue: DispatchQueue
|
||||
|
||||
init(connection: SocketConnection) {
|
||||
self.connection = connection
|
||||
self.serialQueue = DispatchQueue(label: "org.jitsi.meet.broadcast.sampleUploader")
|
||||
|
||||
setupConnection()
|
||||
}
|
||||
|
||||
@discardableResult func send(sample buffer: CMSampleBuffer) -> Bool {
|
||||
guard isReady == true else {
|
||||
return false
|
||||
}
|
||||
|
||||
isReady = false
|
||||
|
||||
dataToSend = prepare(sample: buffer)
|
||||
byteIndex = 0
|
||||
|
||||
serialQueue.async { [weak self] in
|
||||
self?.sendDataChunk()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private extension SampleUploader {
|
||||
|
||||
func setupConnection() {
|
||||
connection.didOpen = { [weak self] in
|
||||
self?.isReady = true
|
||||
}
|
||||
connection.streamHasSpaceAvailable = { [weak self] in
|
||||
self?.serialQueue.async {
|
||||
self?.isReady = !(self?.sendDataChunk() ?? true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult func sendDataChunk() -> Bool {
|
||||
guard let dataToSend = dataToSend else {
|
||||
return false
|
||||
}
|
||||
|
||||
var bytesLeft = dataToSend.count - byteIndex
|
||||
var length = bytesLeft > Constants.bufferMaxLength ? Constants.bufferMaxLength : bytesLeft
|
||||
|
||||
length = dataToSend[byteIndex..<(byteIndex + length)].withUnsafeBytes {
|
||||
guard let ptr = $0.bindMemory(to: UInt8.self).baseAddress else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return connection.writeToStream(buffer: ptr, maxLength: length)
|
||||
}
|
||||
|
||||
if length > 0 {
|
||||
byteIndex += length
|
||||
bytesLeft -= length
|
||||
|
||||
if bytesLeft == 0 {
|
||||
self.dataToSend = nil
|
||||
byteIndex = 0
|
||||
}
|
||||
} else {
|
||||
print("writeBufferToStream failure")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func prepare(sample buffer: CMSampleBuffer) -> Data? {
|
||||
guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
|
||||
print("image buffer not available")
|
||||
return nil
|
||||
}
|
||||
|
||||
CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
|
||||
|
||||
let scaleFactor = 2.0
|
||||
let width = CVPixelBufferGetWidth(imageBuffer)/Int(scaleFactor)
|
||||
let height = CVPixelBufferGetHeight(imageBuffer)/Int(scaleFactor)
|
||||
let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0
|
||||
|
||||
let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0/scaleFactor), y: CGFloat(1.0/scaleFactor))
|
||||
let bufferData = self.jpegData(from: imageBuffer, scale: scaleTransform)
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
|
||||
|
||||
guard let messageData = bufferData else {
|
||||
print("corrupted image buffer")
|
||||
return nil
|
||||
}
|
||||
|
||||
let httpResponse = CFHTTPMessageCreateResponse(nil, 200, nil, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Content-Length" as CFString, String(messageData.count) as CFString)
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Width" as CFString, String(width) as CFString)
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Height" as CFString, String(height) as CFString)
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Orientation" as CFString, String(orientation) as CFString)
|
||||
|
||||
CFHTTPMessageSetBody(httpResponse, messageData as CFData)
|
||||
|
||||
let serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse)?.takeRetainedValue() as Data?
|
||||
|
||||
return serializedMessage
|
||||
}
|
||||
|
||||
func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? {
|
||||
var image = CIImage(cvPixelBuffer: buffer)
|
||||
image = image.transformed(by: scaleTransform)
|
||||
|
||||
guard let colorSpace = image.colorSpace else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0]
|
||||
let imageData = SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options)
|
||||
|
||||
return imageData
|
||||
}
|
||||
}
|
||||
205
ios/app/broadcast-extension/SocketConnection.swift
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright @ 2021-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 Foundation
|
||||
|
||||
class SocketConnection: NSObject {
|
||||
var didOpen: (() -> Void)?
|
||||
var didClose: ((Error?) -> Void)?
|
||||
var streamHasSpaceAvailable: (() -> Void)?
|
||||
|
||||
private let filePath: String
|
||||
private var socketHandle: Int32 = -1
|
||||
private var address: sockaddr_un?
|
||||
|
||||
private var inputStream: InputStream?
|
||||
private var outputStream: OutputStream?
|
||||
|
||||
private var networkQueue: DispatchQueue?
|
||||
private var shouldKeepRunning = false
|
||||
|
||||
init?(filePath path: String) {
|
||||
filePath = path
|
||||
socketHandle = Darwin.socket(AF_UNIX, SOCK_STREAM, 0)
|
||||
|
||||
guard socketHandle != -1 else {
|
||||
print("failure: create socket")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func open() -> Bool {
|
||||
print("open socket connection")
|
||||
|
||||
guard FileManager.default.fileExists(atPath: filePath) else {
|
||||
print("failure: socket file missing")
|
||||
return false
|
||||
}
|
||||
|
||||
guard setupAddress() == true else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard connectSocket() == true else {
|
||||
return false
|
||||
}
|
||||
|
||||
setupStreams()
|
||||
|
||||
inputStream?.open()
|
||||
outputStream?.open()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func close() {
|
||||
unscheduleStreams()
|
||||
|
||||
inputStream?.delegate = nil
|
||||
outputStream?.delegate = nil
|
||||
|
||||
inputStream?.close()
|
||||
outputStream?.close()
|
||||
|
||||
inputStream = nil
|
||||
outputStream = nil
|
||||
}
|
||||
|
||||
func writeToStream(buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int {
|
||||
return outputStream?.write(buffer, maxLength: length) ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
extension SocketConnection: StreamDelegate {
|
||||
|
||||
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
switch eventCode {
|
||||
case .openCompleted:
|
||||
print("client stream open completed")
|
||||
if aStream == outputStream {
|
||||
didOpen?()
|
||||
}
|
||||
case .hasBytesAvailable:
|
||||
if aStream == inputStream {
|
||||
var buffer: UInt8 = 0
|
||||
let numberOfBytesRead = inputStream?.read(&buffer, maxLength: 1)
|
||||
if numberOfBytesRead == 0 && aStream.streamStatus == .atEnd {
|
||||
print("server socket closed")
|
||||
close()
|
||||
notifyDidClose(error: nil)
|
||||
}
|
||||
}
|
||||
case .hasSpaceAvailable:
|
||||
if aStream == outputStream {
|
||||
streamHasSpaceAvailable?()
|
||||
}
|
||||
case .errorOccurred:
|
||||
print("client stream error occured: \(String(describing: aStream.streamError))")
|
||||
close()
|
||||
notifyDidClose(error: aStream.streamError)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SocketConnection {
|
||||
|
||||
func setupAddress() -> Bool {
|
||||
var addr = sockaddr_un()
|
||||
guard filePath.count < MemoryLayout.size(ofValue: addr.sun_path) else {
|
||||
print("failure: fd path is too long")
|
||||
return false
|
||||
}
|
||||
|
||||
_ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in
|
||||
filePath.withCString {
|
||||
strncpy(ptr, $0, filePath.count)
|
||||
}
|
||||
}
|
||||
|
||||
address = addr
|
||||
return true
|
||||
}
|
||||
|
||||
func connectSocket() -> Bool {
|
||||
guard var addr = address else {
|
||||
return false
|
||||
}
|
||||
|
||||
let status = withUnsafePointer(to: &addr) { ptr in
|
||||
ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
||||
Darwin.connect(socketHandle, $0, socklen_t(MemoryLayout<sockaddr_un>.size))
|
||||
}
|
||||
}
|
||||
|
||||
guard status == noErr else {
|
||||
print("failure: \(status)")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func setupStreams() {
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
|
||||
CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketHandle, &readStream, &writeStream)
|
||||
|
||||
inputStream = readStream?.takeRetainedValue()
|
||||
inputStream?.delegate = self
|
||||
inputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
|
||||
|
||||
outputStream = writeStream?.takeRetainedValue()
|
||||
outputStream?.delegate = self
|
||||
outputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
|
||||
|
||||
scheduleStreams()
|
||||
}
|
||||
|
||||
func scheduleStreams() {
|
||||
shouldKeepRunning = true
|
||||
|
||||
networkQueue = DispatchQueue.global(qos: .userInitiated)
|
||||
networkQueue?.async { [weak self] in
|
||||
self?.inputStream?.schedule(in: .current, forMode: .common)
|
||||
self?.outputStream?.schedule(in: .current, forMode: .common)
|
||||
|
||||
var isRunning = false
|
||||
|
||||
repeat {
|
||||
isRunning = self?.shouldKeepRunning ?? false && RunLoop.current.run(mode: .default, before: .distantFuture)
|
||||
} while (isRunning)
|
||||
}
|
||||
}
|
||||
|
||||
func unscheduleStreams() {
|
||||
networkQueue?.sync { [weak self] in
|
||||
self?.inputStream?.remove(from: .current, forMode: .common)
|
||||
self?.outputStream?.remove(from: .current, forMode: .common)
|
||||
}
|
||||
|
||||
shouldKeepRunning = false
|
||||
}
|
||||
|
||||
func notifyDidClose(error: Error?) {
|
||||
if didClose != nil {
|
||||
didClose?(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
10
ios/app/broadcast-extension/extension.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.org.jitsi.meet.appgroup</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
[builder setFeatureFlag:@"resolution" withValue:@(360)];
|
||||
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
builder.welcomePageEnabled = YES;
|
||||
|
||||
@@ -51,7 +52,7 @@
|
||||
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
|
||||
NSLog(@"Enabling Firebase");
|
||||
[FIRApp configure];
|
||||
// Crashlytics defaults to disabled wirth the FirebaseCrashlyticsCollectionEnabled Info.plist key.
|
||||
// Crashlytics defaults to disabled with the FirebaseCrashlyticsCollectionEnabled Info.plist key.
|
||||
[[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:![jitsiMeet isCrashReportingDisabled]];
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.0.0</string>
|
||||
<string>21.2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -45,6 +45,8 @@
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>FirebaseCrashlyticsCollectionEnabled</key>
|
||||
<string>false</string>
|
||||
<key>FirebaseScreenReportingEnabled</key>
|
||||
<false/>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
@@ -66,14 +68,18 @@
|
||||
<string>See your scheduled meetings in the app.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Participate in meetings with video.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Participate in meetings with voice.</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Local network is used for establishing Peer-to-Peer connections.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Participate in meetings with voice.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>org.jitsi.JitsiMeet.ios.conference</string>
|
||||
</array>
|
||||
<key>RTCAppGroupIdentifier</key>
|
||||
<string>group.org.jitsi.meet.appgroup</string>
|
||||
<key>RTCScreenSharingExtension</key>
|
||||
<string>org.jitsi.meet.broadcast.extension</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
@@ -99,7 +105,5 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>FirebaseCrashlyticsCollectionEnabled</key>
|
||||
<string>false</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -131,6 +131,10 @@
|
||||
NSLog(@"%@%@", @"Chat toggled: ", data);
|
||||
}
|
||||
|
||||
- (void)videoMutedChanged:(NSDictionary *)data {
|
||||
NSLog(@"%@%@", @"Video muted changed: ", data[@"muted"]);
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (void)terminate {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.0.0</string>
|
||||
<string>21.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.0.0</string>
|
||||
<string>21.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -35,7 +35,7 @@ fi
|
||||
|
||||
echo "PR_REPO_SLUG=${PR_REPO_SLUG} PR_BRANCH=${PR_BRANCH}"
|
||||
|
||||
# do the marge and git log
|
||||
# do the merge and git log
|
||||
|
||||
if [ $PR_BRANCH != "master" ]; then
|
||||
echo "Will merge ${PR_REPO_SLUG}/${PR_BRANCH} into master"
|
||||
|
||||
@@ -90,7 +90,7 @@ echo "importing dev-key.p12"
|
||||
security import ${CERT_DIR}/dev-key.p12 -k ios-build.keychain -P $IOS_SIGNING_CERT_PASSWORD -A
|
||||
|
||||
echo "will set-key-partition-list"
|
||||
# Fix for OS X Sierra that hungs in the codesign step
|
||||
# Fix for OS X Sierra that hangs in the codesign step
|
||||
security set-key-partition-list -S apple-tool:,apple: -s -k $ENCRYPTION_PASSWORD ios-build.keychain > /dev/null
|
||||
echo "done set-key-partition-list"
|
||||
|
||||
|
||||
@@ -9,6 +9,15 @@ platform :ios do
|
||||
# Make sure we are on a clean tree
|
||||
ensure_git_status_clean
|
||||
|
||||
# Connect to Apple Store Connect
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV["ASC_KEY_ID"],
|
||||
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||
key_content: ENV["ASC_KEY_CONTENT"],
|
||||
duration: 1200,
|
||||
in_house: false
|
||||
)
|
||||
|
||||
# Set the app identifier
|
||||
update_app_identifier(
|
||||
xcodeproj: "app/app.xcodeproj",
|
||||
@@ -16,6 +25,20 @@ platform :ios do
|
||||
app_identifier: "com.atlassian.JitsiMeet.ios"
|
||||
)
|
||||
|
||||
# Set the broadcast extension identifier
|
||||
update_app_identifier(
|
||||
xcodeproj: "app/app.xcodeproj",
|
||||
plist_path: "broadcast-extension/Info.plist",
|
||||
app_identifier: "com.atlassian.JitsiMeet.ios.broadcast"
|
||||
)
|
||||
update_info_plist(
|
||||
xcodeproj: "app/app.xcodeproj",
|
||||
plist_path: "src/Info.plist",
|
||||
block: proc do |plist|
|
||||
plist["RTCScreenSharingExtension"] = "com.atlassian.JitsiMeet.ios.broadcast"
|
||||
end
|
||||
)
|
||||
|
||||
# Set the (watch) app identifier
|
||||
update_app_identifier(
|
||||
xcodeproj: "app/app.xcodeproj",
|
||||
@@ -80,12 +103,24 @@ platform :ios do
|
||||
uses_non_exempt_encryption: false
|
||||
)
|
||||
|
||||
# Upload dSYMs to Crashlytics
|
||||
download_dsyms
|
||||
upload_symbols_to_crashlytics
|
||||
|
||||
# Cleanup
|
||||
clean_build_artifacts
|
||||
reset_git_repo(skip_clean: true)
|
||||
end
|
||||
|
||||
lane :refresh_dsyms do
|
||||
# Connect to Apple Store Connect
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV["ASC_KEY_ID"],
|
||||
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||
key_content: ENV["ASC_KEY_CONTENT"],
|
||||
duration: 1200,
|
||||
in_house: false
|
||||
)
|
||||
|
||||
# Upload dSYMs to Crashlytics
|
||||
download_dsyms(min_version: ENV["DSYMS_MIN_VERSION"]) # Download dSYM files from iTC
|
||||
upload_symbols_to_crashlytics # Upload them to Crashlytics
|
||||
clean_build_artifacts # Delete the local dSYM files
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495D1EC4B6C600B793EE /* POSIX.m */; };
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCA495E1EC4B6C600B793EE /* Proximity.m */; };
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */; };
|
||||
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */; };
|
||||
6C31EDC820C06D490089C899 /* recordingOn.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 6C31EDC720C06D490089C899 /* recordingOn.mp3 */; };
|
||||
6C31EDCA20C06D530089C899 /* recordingOff.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 6C31EDC920C06D530089C899 /* recordingOff.mp3 */; };
|
||||
6F08DF7D4458EE3CF3F36F6D /* libPods-JitsiMeetSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4376CA6886DE68FD7A4294B /* libPods-JitsiMeetSDK.a */; };
|
||||
@@ -85,6 +87,8 @@
|
||||
0BD906E51EC0C00300C8C18E /* JitsiMeetSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JitsiMeetSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeet.h; sourceTree = "<group>"; };
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScheenshareEventEmiter.h; sourceTree = "<group>"; };
|
||||
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScheenshareEventEmiter.m; sourceTree = "<group>"; };
|
||||
6C31EDC720C06D490089C899 /* recordingOn.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = recordingOn.mp3; path = ../../sounds/recordingOn.mp3; sourceTree = "<group>"; };
|
||||
6C31EDC920C06D530089C899 /* recordingOff.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = recordingOff.mp3; path = ../../sounds/recordingOff.mp3; sourceTree = "<group>"; };
|
||||
75635B0820751D6D00F29C9F /* joined.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = joined.wav; path = ../../sounds/joined.wav; sourceTree = "<group>"; };
|
||||
@@ -194,7 +198,6 @@
|
||||
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
|
||||
C69EFA02209A0EFD0027712B /* callkit */,
|
||||
A4A934E7212F3AB8001E9388 /* dropbox */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
DE438CD82350934700DD541D /* JavaScriptSandbox.m */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
@@ -231,6 +234,9 @@
|
||||
C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */,
|
||||
C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */,
|
||||
C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */,
|
||||
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */,
|
||||
);
|
||||
path = src;
|
||||
sourceTree = "<group>";
|
||||
@@ -298,6 +304,7 @@
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
|
||||
DE81A2DE2317ED5400AE1940 /* JitsiMeetBaseLogHandler.h in Headers */,
|
||||
DEA9F284258A5D9900D4CD74 /* JitsiMeetSDK.h in Headers */,
|
||||
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */,
|
||||
DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
|
||||
@@ -466,6 +473,7 @@
|
||||
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */,
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
||||
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */,
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
|
||||
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
|
||||
DE81A2D52316AC4D00AE1940 /* JitsiMeetLogger.m in Sources */,
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
- (void)sendHangUp;
|
||||
- (void)sendSetAudioMuted:(BOOL)muted;
|
||||
- (void)sendEndpointTextMessage:(NSString*)message :(NSString*)to;
|
||||
- (void)toggleScreenShare;
|
||||
- (void)toggleScreenShare:(BOOL)enabled;
|
||||
- (void)retrieveParticipantsInfo:(void (^)(NSArray*))completion;
|
||||
- (void)openChat:(NSString*)to;
|
||||
- (void)closeChat;
|
||||
- (void)sendChatMessage:(NSString*)message :(NSString*)to ;
|
||||
- (void)sendSetVideoMuted:(BOOL)muted;
|
||||
|
||||
@end
|
||||
|
||||
@@ -26,6 +26,7 @@ static NSString * const retrieveParticipantsInfoAction = @"org.jitsi.meet.RETRIE
|
||||
static NSString * const openChatAction = @"org.jitsi.meet.OPEN_CHAT";
|
||||
static NSString * const closeChatAction = @"org.jitsi.meet.CLOSE_CHAT";
|
||||
static NSString * const sendChatMessageAction = @"org.jitsi.meet.SEND_CHAT_MESSAGE";
|
||||
static NSString * const setVideoMutedAction = @"org.jitsi.meet.SET_VIDEO_MUTED";
|
||||
|
||||
@implementation ExternalAPI
|
||||
|
||||
@@ -47,7 +48,8 @@ RCT_EXPORT_MODULE();
|
||||
@"RETRIEVE_PARTICIPANTS_INFO": retrieveParticipantsInfoAction,
|
||||
@"OPEN_CHAT": openChatAction,
|
||||
@"CLOSE_CHAT": closeChatAction,
|
||||
@"SEND_CHAT_MESSAGE": sendChatMessageAction
|
||||
@"SEND_CHAT_MESSAGE": sendChatMessageAction,
|
||||
@"SET_VIDEO_MUTED" : setVideoMutedAction
|
||||
};
|
||||
};
|
||||
|
||||
@@ -70,7 +72,8 @@ RCT_EXPORT_MODULE();
|
||||
retrieveParticipantsInfoAction,
|
||||
openChatAction,
|
||||
closeChatAction,
|
||||
sendChatMessageAction
|
||||
sendChatMessageAction,
|
||||
setVideoMutedAction
|
||||
];
|
||||
}
|
||||
|
||||
@@ -161,8 +164,11 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
|
||||
[self sendEventWithName:sendEndpointTextMessageAction body:data];
|
||||
}
|
||||
|
||||
- (void)toggleScreenShare {
|
||||
[self sendEventWithName:toggleScreenShareAction body:nil];
|
||||
- (void)toggleScreenShare:(BOOL)enabled {
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
data[@"enabled"] = [NSNumber numberWithBool:enabled];
|
||||
|
||||
[self sendEventWithName:toggleScreenShareAction body:data];
|
||||
}
|
||||
|
||||
- (void)retrieveParticipantsInfo:(void (^)(NSArray*))completionHandler {
|
||||
@@ -193,4 +199,11 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
|
||||
[self sendEventWithName:sendChatMessageAction body:data];
|
||||
}
|
||||
|
||||
- (void)sendSetVideoMuted:(BOOL)muted {
|
||||
NSDictionary *data = @{ @"muted": [NSNumber numberWithBool:muted]};
|
||||
|
||||
[self sendEventWithName:setVideoMutedAction body:data];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.2.0</string>
|
||||
<string>3.5.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#import "RCTBridgeWrapper.h"
|
||||
#import "ReactUtils.h"
|
||||
#import "RNSplashScreen.h"
|
||||
#import "ScheenshareEventEmiter.h"
|
||||
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
#import <WebRTC/RTCLogging.h>
|
||||
@@ -31,6 +32,7 @@
|
||||
@implementation JitsiMeet {
|
||||
RCTBridgeWrapper *_bridgeWrapper;
|
||||
NSDictionary *_launchOptions;
|
||||
ScheenshareEventEmiter *_screenshareEventEmiter;
|
||||
}
|
||||
|
||||
#pragma mak - This class is a singleton
|
||||
@@ -50,6 +52,9 @@
|
||||
if (self = [super init]) {
|
||||
// Initialize the on and only bridge for interfacing with React Native.
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
|
||||
// Initialize the listener for handling start/stop screensharing notifications.
|
||||
_screenshareEventEmiter = [[ScheenshareEventEmiter alloc] init];
|
||||
|
||||
// Register a fatal error handler for React.
|
||||
registerReactFatalErrorHandler();
|
||||
|
||||
@@ -71,6 +71,16 @@
|
||||
- (void)setFeatureFlag:(NSString *_Nonnull)flag withBoolean:(BOOL)value;
|
||||
- (void)setFeatureFlag:(NSString *_Nonnull)flag withValue:(id _Nonnull)value;
|
||||
|
||||
/**
|
||||
* CallKit call handle, to be used when implementing incoming calls.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *callHandle;
|
||||
|
||||
/**
|
||||
* CallKit call UUID, to be used when implementing incoming calls.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSUUID *callUUID;
|
||||
|
||||
@end
|
||||
|
||||
@interface JitsiMeetConferenceOptions : NSObject
|
||||
@@ -92,6 +102,9 @@
|
||||
|
||||
@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo;
|
||||
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *callHandle;
|
||||
@property (nonatomic, copy, nullable, readonly) NSUUID *callUUID;
|
||||
|
||||
+ (instancetype _Nonnull)fromBuilder:(void (^_Nonnull)(JitsiMeetConferenceOptionsBuilder *_Nonnull))initBlock;
|
||||
- (instancetype _Nonnull)init NS_UNAVAILABLE;
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
_videoMuted = nil;
|
||||
|
||||
_userInfo = nil;
|
||||
|
||||
_callHandle = nil;
|
||||
_callUUID = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -168,6 +171,9 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
_featureFlags = [NSDictionary dictionaryWithDictionary:builder.featureFlags];
|
||||
|
||||
_userInfo = builder.userInfo;
|
||||
|
||||
_callHandle = builder.callHandle;
|
||||
_callUUID = builder.callUUID;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -205,6 +211,12 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (_subject != nil) {
|
||||
config[@"subject"] = self.subject;
|
||||
}
|
||||
if (_callHandle != nil) {
|
||||
config[@"callHandle"] = self.callHandle;
|
||||
}
|
||||
if (_callUUID != nil) {
|
||||
config[@"callUUID"] = [self.callUUID UUIDString];
|
||||
}
|
||||
|
||||
NSMutableDictionary *urlProps = [[NSMutableDictionary alloc] init];
|
||||
|
||||
|
||||
@@ -39,10 +39,11 @@
|
||||
- (void)hangUp;
|
||||
- (void)setAudioMuted:(BOOL)muted;
|
||||
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
|
||||
- (void)toggleScreenShare;
|
||||
- (void)toggleScreenShare:(BOOL)enabled;
|
||||
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler;
|
||||
- (void)openChat:(NSString * _Nullable)to;
|
||||
- (void)closeChat;
|
||||
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
|
||||
- (void)setVideoMuted:(BOOL)muted;
|
||||
|
||||
@end
|
||||
|
||||
@@ -130,9 +130,9 @@ static void initializeViewsMap() {
|
||||
[externalAPI sendEndpointTextMessage:message :to];
|
||||
}
|
||||
|
||||
- (void)toggleScreenShare {
|
||||
- (void)toggleScreenShare:(BOOL)enabled {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI toggleScreenShare];
|
||||
[externalAPI toggleScreenShare:enabled];
|
||||
}
|
||||
|
||||
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
|
||||
@@ -155,6 +155,11 @@ static void initializeViewsMap() {
|
||||
[externalAPI sendChatMessage:message :to];
|
||||
}
|
||||
|
||||
- (void)setVideoMuted:(BOOL)muted {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendSetVideoMuted:muted];
|
||||
}
|
||||
|
||||
#pragma mark Private methods
|
||||
|
||||
/**
|
||||
@@ -184,7 +189,7 @@ static void initializeViewsMap() {
|
||||
// conference. However, React and, respectively,
|
||||
// appProperties/initialProperties are declarative expressions i.e. one and
|
||||
// the same URL will not trigger an automatic re-render in the JavaScript
|
||||
// source code. The workaround implemented bellow introduces imperativeness
|
||||
// source code. The workaround implemented below introduces imperativeness
|
||||
// in React Component props by defining a unique value per invocation.
|
||||
props[@"timestamp"] = @(mach_absolute_time());
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
*
|
||||
* The `data` dictionary contains `message`, `senderId` and `isPrivate` keys.
|
||||
*/
|
||||
- (void)chatMessaageReceived:(NSDictionary *)data;
|
||||
- (void)chatMessageReceived:(NSDictionary *)data;
|
||||
|
||||
/**
|
||||
* Called when the chat dialog is displayed/hidden.
|
||||
@@ -104,4 +104,11 @@
|
||||
*/
|
||||
- (void)chatToggled:(NSDictionary *)data;
|
||||
|
||||
/**
|
||||
* Called when videoMuted state changed.
|
||||
*
|
||||
* The `data` dictionary contains a `muted` key with state of the videoMuted for the localParticipant.
|
||||
*/
|
||||
- (void)videoMutedChanged:(NSDictionary *)data;
|
||||
|
||||
@end
|
||||
|
||||
25
ios/sdk/src/ScheenshareEventEmiter.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright @ 2021-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 <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ScheenshareEventEmiter : NSObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
72
ios/sdk/src/ScheenshareEventEmiter.m
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright @ 2021-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 "ScheenshareEventEmiter.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "ExternalAPI.h"
|
||||
|
||||
NSNotificationName const kBroadcastStartedNotification = @"iOS_BroadcastStarted";
|
||||
NSNotificationName const kBroadcastStoppedNotification = @"iOS_BroadcastStopped";
|
||||
|
||||
@implementation ScheenshareEventEmiter {
|
||||
CFNotificationCenterRef _notificationCenter;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
|
||||
[self setupObserver];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self clearObserver];
|
||||
}
|
||||
|
||||
// MARK: Private Methods
|
||||
|
||||
- (void)setupObserver {
|
||||
CFNotificationCenterAddObserver(_notificationCenter, (__bridge const void *)(self), broadcastStartedNotificationCallback, (__bridge CFStringRef)kBroadcastStartedNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
|
||||
CFNotificationCenterAddObserver(_notificationCenter, (__bridge const void *)(self), broadcastStoppedNotificationCallback, (__bridge CFStringRef)kBroadcastStoppedNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
|
||||
}
|
||||
|
||||
- (void)clearObserver {
|
||||
CFNotificationCenterRemoveObserver(_notificationCenter, (__bridge const void *)(self), (__bridge CFStringRef)kBroadcastStartedNotification, NULL);
|
||||
CFNotificationCenterRemoveObserver(_notificationCenter, (__bridge const void *)(self), (__bridge CFStringRef)kBroadcastStoppedNotification, NULL);
|
||||
}
|
||||
|
||||
void broadcastStartedNotificationCallback(CFNotificationCenterRef center,
|
||||
void *observer,
|
||||
CFStringRef name,
|
||||
const void *object,
|
||||
CFDictionaryRef userInfo) {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI toggleScreenShare:true];
|
||||
}
|
||||
|
||||
void broadcastStoppedNotificationCallback(CFNotificationCenterRef center,
|
||||
void *observer,
|
||||
CFStringRef name,
|
||||
const void *object,
|
||||
CFDictionaryRef userInfo) {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI toggleScreenShare:false];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -76,7 +76,7 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
|
||||
// Avoid mute actions ping-pong: if the mute action was caused by
|
||||
// the JS side (we requested a transaction) don't call the delegate
|
||||
// method. If it was called by the provder itself (when the user presses
|
||||
// method. If it was called by the provider itself (when the user presses
|
||||
// the mute button in the CallKit view) then call the delegate method.
|
||||
//
|
||||
// NOTE: don't try to be clever and remove this. Been there, done that.
|
||||
|
||||
@@ -29,7 +29,7 @@ public protocol PiPViewCoordinatorDelegate: class {
|
||||
/// when is presented in Picure in Picture mode.
|
||||
public class PiPViewCoordinator {
|
||||
|
||||
/// Limits the boundries of view position on screen when minimized
|
||||
/// Limits the boundaries of view position on screen when minimized
|
||||
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
|
||||
left: 5,
|
||||
bottom: 5,
|
||||
|
||||