Compare commits

..

1 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
9f4c036174 fix(blur) fix model paths 2021-02-18 15:36:37 +01:00
319 changed files with 3135 additions and 6295 deletions

View File

@@ -5,9 +5,8 @@ build/*
# modify as little as possible.
flow-typed/*
libs/*
resources/*
react/features/stream-effects/virtual-background/vendor/*
load-test/*
react/features/stream-effects/blur/vendor/*
# 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

View File

@@ -12,6 +12,5 @@ jobs:
with:
node-version: '12.x'
- run: npm install
- run: git status -s --untracked-files=no
- run: npm run lint
- run: make

View File

@@ -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/virtual-background/vendor/tflite
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
TFLITE_WASM = react/features/stream-effects/blur/vendor/tflite
MEET_MODELS_DIR = react/features/stream-effects/blur/vendor/models/
NODE_SASS = ./node_modules/.bin/sass
NPM = npm
OUTPUT_DIR = .
@@ -51,6 +51,10 @@ 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)

View File

@@ -122,7 +122,7 @@ gradle.projectsEvaluated {
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.getProcessManifestProvider().get().doLast {
def outputDir = multiApkManifestOutputDirectory.get().asFile
def outputDir = manifestOutputDirectory.get().asFile
def manifestPath = new File(outputDir, 'AndroidManifest.xml')
def charset = 'UTF-8'
def text

View File

@@ -26,4 +26,4 @@ android.useAndroidX=true
android.enableJetifier=true
appVersion=21.0.0
sdkVersion=3.2.0
sdkVersion=3.1.0

View File

@@ -132,7 +132,6 @@ public class JitsiMeetOngoingConferenceService extends Service
public void onCurrentConferenceChanged(String conferenceUrl) {
if (conferenceUrl == null) {
stopSelf();
OngoingNotification.resetStartingtime();
JitsiMeetLogger.i(TAG + "Service stopped");
}
}

View File

@@ -43,7 +43,6 @@ 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) {
@@ -86,10 +85,6 @@ 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))
@@ -97,8 +92,6 @@ class OngoingNotification {
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setWhen(startingTime)
.setUsesChronometer(true)
.setAutoCancel(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
@@ -117,10 +110,6 @@ 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());

View File

@@ -99,7 +99,6 @@ import {
destroyLocalTracks,
getLocalJitsiAudioTrack,
getLocalJitsiVideoTrack,
getLocalTracks,
isLocalCameraTrackMuted,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
@@ -116,7 +115,7 @@ import {
submitFeedback
} from './react/features/feedback';
import { showNotification } from './react/features/notifications';
import { mediaPermissionPromptVisibilityChanged, toggleSlowGUMOverlay } from './react/features/overlay';
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
import { suspendDetected } from './react/features/power-monitor';
import {
initPrejoin,
@@ -126,7 +125,7 @@ import {
} from './react/features/prejoin';
import { disableReceiver, stopReceiver } from './react/features/remote-control';
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
import { setSharedVideoStatus } from './react/features/shared-video/actions';
import { setSharedVideoStatus } from './react/features/shared-video';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createPresenterEffect } from './react/features/stream-effects/presenter';
import { endpointMessageReceived } from './react/features/subtitles';
@@ -474,13 +473,18 @@ 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
@@ -498,11 +502,6 @@ export default {
);
}
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.SLOW_GET_USER_MEDIA,
() => APP.store.dispatch(toggleSlowGUMOverlay(true))
);
let tryCreateLocalTracks;
// On Electron there is no permission prompt for granting permissions. That's why we don't need to
@@ -520,10 +519,8 @@ export default {
return createLocalTracksF({
devices: [ 'audio' ],
timeout,
firePermissionPromptIsShownEvent: true,
fireSlowPromiseEvent: true
})
timeout
}, true)
.then(([ audioStream ]) =>
[ desktopStream, audioStream ])
.catch(error => {
@@ -539,10 +536,8 @@ export default {
return requestedAudio
? createLocalTracksF({
devices: [ 'audio' ],
timeout,
firePermissionPromptIsShownEvent: true,
fireSlowPromiseEvent: true
})
timeout
}, true)
: [];
})
.catch(error => {
@@ -556,10 +551,8 @@ export default {
} else {
tryCreateLocalTracks = createLocalTracksF({
devices: initialDevices,
timeout,
firePermissionPromptIsShownEvent: true,
fireSlowPromiseEvent: true
})
timeout
}, true)
.catch(err => {
if (requestedAudio && requestedVideo) {
@@ -581,10 +574,8 @@ export default {
return (
createLocalTracksF({
devices: [ 'audio' ],
timeout,
firePermissionPromptIsShownEvent: true,
fireSlowPromiseEvent: true
}));
timeout
}, true));
} else if (requestedAudio && !requestedVideo) {
errors.audioOnlyError = err;
@@ -607,9 +598,8 @@ export default {
return requestedVideo
? createLocalTracksF({
devices: [ 'video' ],
firePermissionPromptIsShownEvent: true,
fireSlowPromiseEvent: true
})
timeout
}, true)
: [];
})
.catch(err => {
@@ -629,7 +619,6 @@ export default {
// the user inputs their credentials, but the dialog would be
// overshadowed by the overlay.
tryCreateLocalTracks.then(tracks => {
APP.store.dispatch(toggleSlowGUMOverlay(false));
APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false));
return tracks;
@@ -820,16 +809,12 @@ export default {
return this._setLocalAudioVideoStreams(tracks);
}
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(roomName, initialOptions);
let localTracks = tracks;
const [ tracks, con ] = await this.createInitialLocalTracksAndConnect(
roomName, initialOptions);
this._initDeviceList(true);
if (initialOptions.startWithAudioMuted) {
localTracks = localTracks.filter(track => track.getType() !== MEDIA_TYPE.AUDIO);
}
return this.startConference(con, localTracks);
return this.startConference(con, tracks);
},
/**
@@ -897,7 +882,7 @@ export default {
showUI && APP.store.dispatch(notifyMicError(error));
};
createLocalTracksF({ devices: [ 'audio' ] })
createLocalTracksF({ devices: [ 'audio' ] }, false)
.then(([ audioTrack ]) => audioTrack)
.catch(error => {
maybeShowErrorDialog(error);
@@ -1011,7 +996,7 @@ export default {
//
// FIXME when local track creation is moved to react/redux
// it should take care of the use case described above
createLocalTracksF({ devices: [ 'video' ] })
createLocalTracksF({ devices: [ 'video' ] }, false)
.then(([ videoTrack ]) => videoTrack)
.catch(error => {
// FIXME should send some feedback to the API on error ?
@@ -1020,11 +1005,7 @@ export default {
// Rollback the video muted status by using null track
return null;
})
.then(videoTrack => {
logger.debug(`muteVideo: calling useVideoStream for track: ${videoTrack}`);
return this.useVideoStream(videoTrack);
});
.then(videoTrack => this.useVideoStream(videoTrack));
} else {
// FIXME show error dialog if it fails (should be handled by react)
muteLocalVideo(mute);
@@ -1324,11 +1305,7 @@ export default {
this._getConferenceOptions());
APP.store.dispatch(conferenceWillJoin(room));
// Filter out the tracks that are muted.
const tracks = localTracks.filter(track => !track.isMuted());
this._setLocalAudioVideoStreams(tracks);
this._setLocalAudioVideoStreams(localTracks);
this._room = room; // FIXME do not use this
sendLocalParticipant(APP.store, room);
@@ -1347,11 +1324,8 @@ 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);
@@ -1371,8 +1345,6 @@ export default {
* @returns {Promise}
*/
useVideoStream(newTrack) {
logger.debug(`useVideoStream: ${newTrack}`);
return new Promise((resolve, reject) => {
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
const state = APP.store.getState();
@@ -1382,20 +1354,14 @@ 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(error => {
logger.error(`useVideoStream failed on the prejoin screen: ${error}`);
reject(error);
})
.catch(reject)
.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);
@@ -1405,10 +1371,7 @@ export default {
this.setVideoMuteStatus(this.isLocalVideoMuted());
})
.then(resolve)
.catch(error => {
logger.error(`useVideoStream failed: ${error}`);
reject(error);
})
.catch(reject)
.then(onFinish);
});
});
@@ -1555,11 +1518,7 @@ export default {
if (didHaveVideo) {
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
.then(([ stream ]) => {
logger.debug(`_turnScreenSharingOff using ${stream} for useVideoStream`);
return this.useVideoStream(stream);
})
.then(([ stream ]) => this.useVideoStream(stream))
.catch(error => {
logger.error('failed to switch back to local video', error);
@@ -1570,11 +1529,7 @@ export default {
);
});
} else {
promise = promise.then(() => {
logger.debug('_turnScreenSharingOff using null for useVideoStream');
return this.useVideoStream(null);
});
promise = promise.then(() => this.useVideoStream(null));
}
return promise.then(
@@ -1585,8 +1540,6 @@ export default {
},
error => {
this.videoSwitchInProgress = false;
logger.error(`_turnScreenSharingOff failed: ${error}`);
throw error;
});
},
@@ -1607,7 +1560,6 @@ export default {
* @return {Promise.<T>}
*/
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
logger.debug(`toggleScreenSharing: ${toggle}`);
if (this.videoSwitchInProgress) {
return Promise.reject('Switch in progress.');
}
@@ -1674,8 +1626,6 @@ export default {
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
@@ -1840,7 +1790,6 @@ export default {
const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
if (desktopVideoStream) {
logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
await this.useVideoStream(desktopVideoStream);
}
@@ -2044,11 +1993,7 @@ export default {
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (track, participantThatMutedUs) => {
if (participantThatMutedUs) {
APP.store.dispatch(participantMutedUs(participantThatMutedUs, track));
if (this.isSharingScreen && track.isVideoTrack()) {
logger.debug('TRACK_MUTE_CHANGED while screen sharing');
this._turnScreenSharingOff(false);
}
APP.store.dispatch(participantMutedUs(participantThatMutedUs));
}
});
@@ -2200,26 +2145,8 @@ export default {
}
);
room.on(JitsiConferenceEvents.STARTED_MUTED, () => {
const audioMuted = room.isStartAudioMuted();
const videoMuted = room.isStartVideoMuted();
const localTracks = getLocalTracks(APP.store.getState()['features/base/tracks']);
const promises = [];
APP.store.dispatch(setAudioMuted(audioMuted));
APP.store.dispatch(setVideoMuted(videoMuted));
// Remove the tracks from the peerconnection.
for (const track of localTracks) {
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.isStartAudioMuted() || room.isStartVideoMuted())
&& APP.UI.notifyInitiallyMuted();
});
room.on(
@@ -2270,7 +2197,7 @@ export default {
.then(effect => this.localVideo.setEffect(effect))
.then(() => {
this.setVideoMuteStatus(false);
logger.log('Switched local video device while screen sharing and the video is unmuted');
logger.log('switched local video device');
this._updateVideoDeviceId();
})
.catch(err => APP.store.dispatch(notifyCameraError(err)));
@@ -2279,7 +2206,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: while screen sharing and the video is muted');
logger.log('switched local video device');
const { height } = this.localVideo.track.getSettings();
this._updateVideoDeviceId();
@@ -2306,20 +2233,12 @@ export default {
return stream;
})
.then(stream => {
logger.log('Switching the local video device.');
return this.useVideoStream(stream);
})
.then(stream => this.useVideoStream(stream))
.then(() => {
logger.log('Switched local video device.');
logger.log('switched local video device');
this._updateVideoDeviceId();
})
.catch(error => {
logger.error(`Switching the local video device failed: ${error}`);
return APP.store.dispatch(notifyCameraError(error));
});
.catch(err => APP.store.dispatch(notifyCameraError(err)));
}
}
);
@@ -2671,7 +2590,6 @@ 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);
}

View File

@@ -240,12 +240,6 @@ var config = {
// 90: 2
// },
// Provides a way to translate the legacy bridge signaling messages, 'LastNChangedEvent',
// 'SelectedEndpointsChangedEvent' and 'ReceiverVideoConstraint' into the new 'ReceiverVideoConstraints' message
// that invokes the new bandwidth allocation algorithm in the bridge which is described here
// - https://github.com/jitsi/jitsi-videobridge/blob/master/doc/allocation.md.
// useNewBandwidthAllocationStrategy: false,
// Specify the settings for video quality optimizations on the client.
// videoQuality: {
// // Provides a way to prevent a video codec from being negotiated on the JVB connection. The codec specified
@@ -412,26 +406,7 @@ var config = {
// enableAutomaticUrlCopy: false,
// Base URL for a Gravatar-compatible service. Defaults to libravatar.
// 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', 'settings', 'raisehand',
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
// ],
// gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/';
// Stats
//
@@ -618,10 +593,6 @@ 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)
@@ -689,11 +660,6 @@ var config = {
// 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

View File

@@ -1,21 +1,3 @@
/**
* Mixins that mimic the way Atlaskit fills the screen with modals at low screen widths.
*/
@mixin full-size-modal-positioner() {
height: 100%;
left: 0;
position: fixed;
top: 0;
max-width: 100%;
width: 100%;
}
@mixin full-size-modal-dialog() {
height: 100%;
max-height: 100%;
border-radius: 0;
}
/**
* Move the @atlaskit/flag container up a little bit so it does not cover the
* toolbar with the first notification.
@@ -72,73 +54,6 @@
* 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.
*/
@media (min-width: 100px) and (max-width: 320px) {
.smiley-input {
display: none;
}
.shift-right .focus-lock > div > div {
@include full-size-modal-positioner();
}
.shift-right .focus-lock [role="dialog"] {
@include full-size-modal-dialog();
}
}
@media (min-width: 480px) and (max-width: 580px) {
.shift-right .focus-lock > div > div {
@include full-size-modal-positioner();
}
.shift-right .focus-lock [role="dialog"] {
@include full-size-modal-dialog();
}
}
@media (min-width: 580px) and (max-width: 680px) {
.mobile-browser {
&.shift-right .focus-lock > div > div {
@include full-size-modal-positioner();
}
&.shift-right .focus-lock [role="dialog"] {
@include full-size-modal-dialog();
}
}
}
// 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;
}
// 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;
}
}

View File

@@ -1,37 +1,26 @@
.audio-preview {
display: inline-block;
&-content {
background: $menuBG;
border-radius: 3px;
font-size: 14px;
background: #2A3A4B;
font-size: 15px;
line-height: 24px;
max-height: 456px;
overflow: auto;
width: 300px;
width: 328px;
}
&-header {
color: #fff;
align-items: center;
display: flex;
margin-top: 8px;
padding: 8px 16px;
padding: 16px;
&-icon {
color: #A4B8D1;
display: inline-block;
svg {
fill: #fff;
}
}
&--bordered {
border-bottom: 1px solid #4C4D50;
}
&-text {
margin-left: 12px;
font-weight: bold;
margin-left: 8px;
}
}
@@ -40,18 +29,19 @@
color: #fff;
cursor: pointer;
display: flex;
padding: 8px 0;
padding: 12px 0;
margin-left: 48px;
&--selected {
background: #131519;
background: #1C2025;
cursor: initial;
margin-left: 0;
padding-left: 18px;
padding-left: 21px;
}
&-text {
color: #fff;
font-size: 15px;
display: inline-block;
line-height: 24px;
text-overflow: ellipsis;
@@ -66,13 +56,12 @@
&:hover {
.audio-preview-entry {
background: #36383C;
background: #3F4E5E;
margin-left: 0;
padding-left: 48px;
&--selected {
padding-left: 18px;
background: $newToolbarBackgroundColor;
padding-left: 21px;
}
}
@@ -85,10 +74,6 @@
}
}
&:last-child {
padding-bottom: 8px;
}
.audio-preview-entry-text {
max-width: 256px;
}
@@ -99,19 +84,18 @@
&:hover {
.audio-preview-entry {
background: #36383C;
background: #3F4E5E;
margin-left: 0;
padding-left: 48px;
&--selected {
background: $newToolbarBackgroundColor;
padding-left: 18px;
padding-left: 21px;
}
}
}
.audio-preview-entry-text {
max-width: 178px;
max-width: 196px;
}
}
@@ -126,7 +110,7 @@
&--check {
background: #31B76A;
margin-right: 16px;
margin-right: 13px;
}
&--exclamation {
@@ -137,11 +121,6 @@
}
}
&-hr {
border-top: 1px solid #4C4D50;
border-bottom: 0;
}
&-test-button {
display: none;
background: #FFF;
@@ -150,16 +129,23 @@
color: #1C2025;
cursor: pointer;
font-weight: 600;
font-size: 15px;
line-height: 24px;
padding: 2px 16px;
padding: 4px 16px;
position: absolute;
right: 16px;
top: 5px;
top: 8px;
}
&-meter-mic {
position: absolute;
right: 16px;
top: 14px;
top: 18px;
}
// Override @atlaskit/InlineDialog container which is made with styled components
& > div:nth-child(2) {
outline: none;
padding: 0;
}
}

View File

@@ -1,5 +1,5 @@
#sideToolbarContainer {
background-color: $chatBackgroundColor;
background-color: $newToolbarBackgroundColor;
box-sizing: border-box;
color: #FFF;
display: flex;
@@ -32,13 +32,6 @@
width: $sidebarWidth;
word-wrap: break-word;
display: flex;
flex-direction: column;
& > :first-child {
margin-top: auto;
}
a {
display: block;
}
@@ -105,12 +98,13 @@
}
.chat-header {
background-color: $chatHeaderBackgroundColor;
height: 70px;
position: relative;
width: 100%;
z-index: 1;
display: flex;
justify-content: flex-end;
justify-content: space-between;
padding: 16px;
align-items: center;
box-sizing: border-box;
@@ -122,85 +116,22 @@
.jitsi-icon {
cursor: pointer;
}
}
.chat-input-container {
padding: 0 16px 16px;
&.populated {
#chat-input {
&:focus-within {
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;
}
}
}
.jitsi-icon > svg {
fill: #A4B8D1;
}
}
#chat-input {
border: 1px solid $chatInputSeparatorColor;
border-top: 1px solid $chatInputSeparatorColor;
display: flex;
padding: 4px;
border-radius: 3px;
padding: 5px 10px;
* {
background-color: transparent;
}
}
.send-button-container {
display: flex;
align-items: center;
}
.send-button {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
width: 40px;
border-radius: 3px;
path {
fill: $chatInputSeparatorColor;
}
}
.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;
}
}
}
.remoteuser {
color: #B8C7E0;
}
@@ -230,47 +161,10 @@
#nickname {
text-align: center;
color: #9d9d9d;
font-size: 16px;
margin: auto 0;
padding: 0 16px;
input {
height: 40px;
}
label {
line-height: 24px;
}
.enter-chat {
display: flex;
align-items: center;
justify-content: center;
margin-top: 16px;
height: 40px;
background: #1B67EC;
border-radius: 3px;
color: #fff;
cursor: pointer;
&.disabled {
color: #757575;
background: #11336E;
pointer-events: none;
}
}
}
.mobile-browser {
#nickname {
input {
height: 48px;
}
.enter-chat {
height: 48px;
}
}
font-size: 18px;
margin-top: 30px;
left: 5px;
right: 5px;
}
.sideToolbarContainer {
@@ -280,8 +174,8 @@
}
.display-name {
font-size: 12px;
font-weight: 600;
font-size: 13px;
font-weight: bold;
margin-bottom: 5px;
white-space: nowrap;
text-overflow: ellipsis;
@@ -331,16 +225,12 @@
}
.messagecontent {
margin: 8px;
margin: 5px 10px;
max-width: 100%;
overflow: hidden;
}
}
.timestamp {
color: #757575;
}
.smiley {
font-size: 14pt;
}
@@ -376,9 +266,7 @@
max-height: 0;
overflow: hidden;
position: absolute;
width: calc(#{$sidebarWidth} - 32px);
margin-bottom: 5px;
margin-left: -5px;
width: $sidebarWidth;
/**
* CSS transitions do not apply for auto dimensions. So to produce the css
@@ -393,8 +281,9 @@
}
#smileysContainer {
background-color: $chatBackgroundColor;
border-top: 1px solid $chatInputSeparatorColor;
background-color: $newToolbarBackgroundColor;
border-bottom: 1px solid;
border-top: 1px solid;
}
}
@@ -500,9 +389,9 @@
&-header {
display: flex;
justify-content: flex-end;
justify-content: space-between;
align-items: center;
margin: 16px;
margin: 16px 16px 24px;
width: calc(100% - 32px);
box-sizing: border-box;
color: #fff;
@@ -513,6 +402,10 @@
.jitsi-icon {
cursor: pointer;
}
.jitsi-icon > svg {
fill: #A4B8D1;
}
}
#chatconversation {
@@ -520,12 +413,6 @@
}
}
.touchmove-hack {
display: flex;
flex: 1;
overflow: auto;
}
/**
* Make header close button more easily tappable on mobile.
*/
@@ -534,6 +421,6 @@
place-items: center;
height: 48px;
width: 48px;
background: #36383C;
background: #2a3a4b;
border-radius: 3px;
}

View File

@@ -7,6 +7,7 @@
}
.drawer-menu {
padding: 0 16px;
max-height: 50vh;
background: #242528;
border-radius: 16px 16px 0 0;
@@ -23,6 +24,12 @@
height: 44px;
cursor: pointer;
@media (hover: hover) and (pointer: fine) {
&:hover {
background-color: $overflowMenuItemHoverBG;
}
}
svg {
fill: none;
}
@@ -60,6 +67,13 @@
align-items: center;
}
@media (hover: hover) and (pointer: fine) {
&:hover {
background-color: $overflowMenuItemHoverBG;
color: $overflowMenuItemHoverColor;
}
}
&.unclickable {
cursor: default;
}
@@ -74,6 +88,42 @@
}
}
.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;

View File

@@ -23,10 +23,44 @@
flex-direction: row;
left: 50%;
position: absolute;
bottom: 10px;
top: 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;
}
}
}
}
.filmstrip-toolbox {
flex-direction: column;
}
.toolbox-button {
&:nth-child(1) {
svg {
fill: $hangupColor;
}
}
.toolbox-icon {
border-radius: 3px;
}
}
}

View File

@@ -6,6 +6,7 @@
min-width: 75px;
text-align: left;
padding: 0px;
width: 180px;
white-space: nowrap;
&__item {

View File

@@ -25,7 +25,7 @@
text {
fill: black;
font-size: 26px;
font-weight: 400;
font-weight: 400;
}
}
@@ -197,6 +197,14 @@
&> div {
margin: 0 12px;
}
.settings-button-small-icon {
right: -8px;
&--hovered {
right: -10px;
}
}
}
}

View File

@@ -1,3 +1,73 @@
@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;
}
}
}
}
}
}
}
@mixin full-size-modal-positioner() {
height: 100%;
left: 0;
position: fixed;
top: 0;
max-width: 100%;
width: 100%;
}
@mixin full-size-modal-dialog() {
height: 100%;
max-height: 100%;
border-radius: 0;
}
@media only screen and (max-width: $verySmallScreen) {
.welcome {
display: block;
@@ -67,9 +137,21 @@
}
.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;
@@ -83,3 +165,25 @@
}
}
}
@media (min-width: 480px) and (max-width: 580px) {
.shift-right .focus-lock > div > div {
@include full-size-modal-positioner();
}
.shift-right .focus-lock [role="dialog"] {
@include full-size-modal-dialog();
}
}
@media (min-width: 580px) and (max-width: 680px) {
.mobile-browser {
&.shift-right .focus-lock > div > div {
@include full-size-modal-positioner();
}
&.shift-right .focus-lock [role="dialog"] {
@include full-size-modal-dialog();
}
}
}

View File

@@ -1,62 +1,84 @@
.settings-button-container {
position: relative;
.settings-button {
&-container {
position: relative;
.toolbox-icon {
align-items: center;
border-radius: 3px;
cursor: pointer;
display: flex;
justify-content: center;
&.disabled, .disabled & {
cursor: initial;
color: #929292;
background-color: #36383c;
.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;
&:hover {
background-color: #36383c;
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;
}
}
}
}
.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;
}
}
}

View File

@@ -33,6 +33,9 @@
&.visible {
bottom: 0;
.toolbox-background {
bottom: 0px;
}
}
&.no-buttons {
@@ -45,222 +48,253 @@
width: calc(100% - #{$sidebarWidth});
}
}
}
.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%;
}
.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;
.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;
right: -5px;
}
}
.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;
}
z-index: $toolbarBackgroundZ;
}
div {
.toolbox-content {
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
}
justify-content: space-between;
margin-left: auto;
margin-right: auto;
padding: 20px 16px;
position: relative;
width: 100%;
z-index: $toolbarZ;
&.unclickable {
cursor: default;
}
&.disabled {
cursor: initial;
color: #929292;
svg {
fill: #929292;
.button-group-center,
.button-group-left,
.button-group-right {
display: flex;
width: 33%;
}
}
@media (hover: hover) and (pointer: fine) {
&.unclickable:hover {
background: inherit;
.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;
}
}
}
}
}
}
}
.beta-tag {
background: #36383C;
border-radius: 3px;
color: #fff;
font-size: 12px;
margin-left: 8px;
padding: 0 4px;
text-transform: uppercase;
}
.overflow-menu-item-icon {
margin-right: 16px;
i {
display: inline;
font-size: 24px;
}
@media (hover: hover) and (pointer: fine) {
i:hover {
background-color: initial;
.button-group-right {
justify-content: flex-end;
}
}
img {
max-width: 24px;
max-height: 24px;
}
.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;
svg {
fill: #fff;
height: 20px;
width: 20px;
}
}
.overflow-menu-item {
align-items: center;
color: $overflowMenuItemColor;
cursor: pointer;
display: flex;
font-size: 14px;
height: 40px;
padding: 5px 12px;
box-sizing: border-box;
div {
display: flex;
flex-direction: row;
align-items: center;
}
.overflow-menu-hr {
border-top: 1px solid #4C4D50;
border-bottom: 0;
margin: 8px 0;
}
@media (hover: hover) and (pointer: fine) {
&:hover {
background-color: $overflowMenuItemHoverBG;
color: $overflowMenuItemHoverColor;
}
}
.toolbox-icon {
display: flex;
border-radius: 3px;
flex-direction: column;
font-size: 24px;
height: $newToolbarSize;
justify-content: center;
width: $newToolbarSize;
&.unclickable {
cursor: default;
}
@media (hover: hover) and (pointer: fine) {
&.unclickable:hover {
background: inherit;
}
}
&.disabled {
cursor: initial;
color: #3b475c;
}
}
@media (hover: hover) and (pointer: fine) {
&:hover {
background: $newToolbarButtonHoverColor;
.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 (max-width: 320px) {
height: 36px;
width: 36px;
}
&.toggled {
background: $newToolbarButtonToggleColor;
}
&.disabled {
cursor: initial !important;
background-color: #36383c !important;
svg {
fill: #929292 !important;
.toolbox-button {
color: $toolbarButtonColor;
cursor: pointer;
display: inline-block;
line-height: $newToolbarSize;
margin: 0 8px;
text-align: center;
}
}
}
.hangup-button {
background-color: $hangupColor;
.toolbar-button-with-badge {
position: relative;
@media (hover: hover) and (pointer: fine) {
&:hover {
background-color: $hangupHoverColor;
.badge-round {
bottom: -5px;
font-size: 12px;
line-height: 20px;
min-width: 20px;
pointer-events: none;
position: absolute;
right: -5px;
}
}
}
svg {
fill: #fff;
.toolbox-button-wth-dialog {
display: inline-block;
}
.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;
}
}
}
}
}
@@ -278,35 +312,3 @@
@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.
*/
.toolbox-content-mobile {
@media (max-width: 500px) {
margin-bottom: 0;
.toolbox-content-items {
border-radius: 0;
display: flex;
justify-content: space-evenly;
padding: 6px 0;
width: 100%;
}
}
}

View File

@@ -4,8 +4,7 @@
* Style variables
*/
$baseFontFamily: -apple-system, BlinkMacSystemFont, 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
$hangupColor:#DD3849;
$hangupHoverColor: #F25363;
$hangupColor: #bf2117;
$hangupFontSize: 2em;
/**
@@ -38,20 +37,20 @@ $presence-idle: rgb(172, 172, 172);
/**
* Toolbar
*/
$newToolbarBackgroundColor: #131519;
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.2);
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.15);
$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);
$menuBG:#242528;
$newToolbarFontSize: 24px;
$newToolbarHangupFontSize: 32px;
$newToolbarSize: 48px;
$newToolbarSize: 40px;
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
$toolbarTitleFontSize: 19px;
$overflowMenuItemColor: #fff;
$overflowMenuItemBackground: #36383C;
$overflowMenuBG: initial;
$overflowMenuItemHoverBG: #313D52;
$overflowMenuItemHoverColor: #B8C7E0;
$overflowMenuItemColor: #B8C7E0;
/**
* Video layout
@@ -91,12 +90,12 @@ $modalTextColor: #333;
* Chat
*/
$chatActionsSeparatorColor: rgb(173, 105, 112);
$chatBackgroundColor: #131519;
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
$chatInputSeparatorColor: #A4B8D1;
$chatLocalMessageBackgroundColor: #484A4F;
$chatLocalMessageBackgroundColor: rgb(4, 98, 178);
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
$chatRemoteMessageBackgroundColor: #242528;
$sidebarWidth: 315px;
$chatRemoteMessageBackgroundColor: rgb(86, 101, 114);
$sidebarWidth: 375px;
/**
* Misc.
@@ -123,8 +122,8 @@ $zindex10: 10;
$reloadZ: 20;
$poweredByZ: 100;
$ringingZ: 300;
$sideToolbarContainerZ: 200;
$toolbarZ: 250;
$sideToolbarContainerZ: 300;
$toolbarZ: 350;
$drawerZ: 351;
$tooltipsZ: 401;
$dropdownMaskZ: 900;

View File

@@ -1,21 +1,18 @@
.video-preview {
background: none;
display: inline-block;
max-height: 344px;
max-height: 290px;
&-container {
background: $menuBG;
border-radius: 3px;
overflow: auto;
padding: 8px;
padding: 16px;
}
&-entry {
cursor: pointer;
height: 168px;
margin-bottom: 8px;
height: 135px;
margin-bottom: 16px;
position: relative;
width: 284px;
width: 240px;
&:last-child {
margin-bottom: 0;
@@ -23,15 +20,13 @@
&--selected {
border: 3px solid #31B76A;
border-radius: 3px;
cursor: default;
height: 162px;
width: 278px;
height: 129px;
width: 234px;
}
}
&-video {
border-radius: 3px;
height: 100%;
object-fit: cover;
width: 100%;
@@ -55,28 +50,21 @@
}
&-label {
bottom: 8px;
color: #fff;
font-size: 13px;
line-height: 20px;
overflow: hidden;
padding: 8px;
position: absolute;
width: 100%;
text-align: center;
text-overflow: ellipsis;
width: 220px;
z-index: 2;
}
&-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;
}
// Override @atlaskit/InlineDialog container which is made with styled components
& > div:nth-child(2) {
outline: none;
padding: 0;
}
}

View File

@@ -208,11 +208,6 @@ body.welcome-page {
cursor: pointer;
font-size: 32px;
}
.toolbox-icon {
height: 24px;
width: 24px;
}
}
.welcome-watermark {

View File

@@ -43,7 +43,6 @@ $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';

View File

@@ -1,44 +0,0 @@
.virtual-background-dialog{
display: inline-flex;
cursor: pointer;
.thumbnail{
object-fit: cover;
padding: 5px;
height: 40px;
width: 40px;
}
.thumbnail-selected{
object-fit: cover;
padding: 5px;
height: 40px;
width: 40px;
border: 2px solid #a4b8d1;
}
.blur-selected{
border: 2px solid #a4b8d1;
}
.virtual-background-none{
font-weight: bold;
padding: 5px;
height: 35px;
width: 35px;
border-radius: 10px;
border: 1px solid #a4b8d1;
text-align: center;
vertical-align: middle;
line-height: 35px;
margin-right: 5px;
}
.none-selected{
font-weight: bold;
padding: 5px;
height: 35px;
width: 35px;
border-radius: 10px;
border: 2px solid #a4b8d1;
text-align: center;
vertical-align: middle;
line-height: 35px;
margin-right: 5px;
}
}

View File

@@ -33,12 +33,4 @@
bottom: 24px;
width: 100%;
}
&__spinner-container {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
}
}

View File

@@ -35,7 +35,7 @@ case "$1" in
db_input critical jitsi-videobridge/jvb-hostname || true
db_go
fi
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME="$RET"
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=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME_OLD=$RET
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

View File

@@ -31,7 +31,7 @@ case "$1" in
purge)
db_get jitsi-meet-prosody/jvb-hostname
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME=$RET
if [ -n "$RET" ]; then
rm -f /etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua

View File

@@ -25,7 +25,7 @@ case "$1" in
. /usr/share/debconf/confmodule
db_get jitsi-meet-prosody/jvb-hostname
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME="$RET"
db_get jitsi-meet-tokens/appid
if [ "$RET" = "false" ] ; then

View File

@@ -30,7 +30,7 @@ case "$1" in
db_input critical jitsi-videobridge/jvb-hostname || true
db_go
fi
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME="$RET"
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=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME_OLD=$RET
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

View File

@@ -32,12 +32,12 @@ case "$1" in
db_go
db_get jitsi-videobridge/jvb-hostname
fi
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME="$RET"
# detect dpkg-reconfigure
RECONFIGURING="false"
db_get jitsi-meet/jvb-hostname
JVB_HOSTNAME_OLD=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME_OLD=$RET
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
RECONFIGURING="true"
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js

View File

@@ -33,7 +33,7 @@ case "$1" in
;;
purge)
db_get jitsi-meet/jvb-hostname
JVB_HOSTNAME=$(echo "$RET" | xargs echo -n)
JVB_HOSTNAME=$RET
if [ -n "$RET" ]; then
rm -f /etc/jitsi/meet/$JVB_HOSTNAME-config.js
rm -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf

View File

@@ -2,6 +2,9 @@
<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>
@@ -39,12 +42,11 @@
</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
ProxyPass /colibri-ws/default-id ws://localhost:9090/colibri-ws/default-id
ProxyPassReverse /colibri-ws/default-id ws://localhost:9090/colibri-ws/default-id
ProxyPassMatch ^/colibri-ws/default-id ws://localhost:9090
RewriteEngine on
RewriteRule ^/([a-zA-Z0-9]+)$ /index.html

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

View File

@@ -168,13 +168,6 @@ var interfaceConfig = {
REMOTE_THUMBNAIL_RATIO: 1, // 1:1
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
/**
* Specify which sharing features should be displayed. If the value is not set
* all sharing features will be shown. You can set [] to disable all.
*/
// SHARING_FEATURES: ['email', 'url', 'dial-in', 'embed'],
SHOW_BRAND_WATERMARK: false,
/**
@@ -198,16 +191,23 @@ var interfaceConfig = {
TOOLBAR_ALWAYS_VISIBLE: false,
/**
* DEPRECATED!
* This config was moved to config.js as `toolbarButtons`.
* 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
*/
// 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_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', 'security'
],
TOOLBAR_TIMEOUT: 4000,

View File

@@ -292,7 +292,7 @@ PODS:
- React
- react-native-splash-screen (3.2.0):
- React
- react-native-webrtc (1.89.1):
- react-native-webrtc (1.87.3):
- React-Core
- react-native-webview (11.0.2):
- React-Core
@@ -563,7 +563,7 @@ SPEC CHECKSUMS:
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-webrtc: ccb0c21eb4fb04326648fbdb4a5d49977e2cf274
react-native-webrtc: dc1208bdca2c4d091f7b57859e69332bff6f1986
react-native-webview: b2542d6fd424bcc3e3b2ec5f854f0abb4ec86c87
React-RCTActionSheet: bcbc311dc3b47bc8efb2737ff0940239a45789a9
React-RCTAnimation: 65f61080ce632f6dea23d52e354ffac9948396c6
@@ -586,4 +586,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 5be5132e41831a98362eeed760558227a4df89ae
COCOAPODS: 1.10.1
COCOAPODS: 1.10.0

View File

@@ -8,10 +8,6 @@
<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>

View File

@@ -23,12 +23,6 @@
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 */; };
4E51B75E25E4115F0038575A /* DarwinNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B75D25E4115F0038575A /* DarwinNotificationCenter.m */; };
4EC49BB725BEDAC100E76218 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC49B8625BED71300E76218 /* ReplayKit.framework */; };
4EC49BBB25BEDAC100E76218 /* SampleHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC49BBA25BEDAC100E76218 /* SampleHandler.m */; };
4EC49BBF25BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
4EC49BCB25BEDB6400E76218 /* SocketConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC49BCA25BEDB6400E76218 /* SocketConnection.m */; };
4EC49BD125BF19CF00E76218 /* SampleUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC49BD025BF19CF00E76218 /* SampleUploader.m */; };
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, ); }; };
@@ -54,13 +48,6 @@
remoteGlobalIDString = 0BEA5C241F7B8F73000D0AB4;
remoteInfo = JitsiMeetCompanion;
};
4EC49BBD25BEDAC100E76218 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4EC49BB525BEDAC100E76218;
remoteInfo = "JitsiMeetBroadcast Extension";
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -98,17 +85,6 @@
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
4EC49B9025BED71300E76218 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
4EC49BBF25BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -139,18 +115,6 @@
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>"; };
4E51B75C25E4115F0038575A /* DarwinNotificationCenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinNotificationCenter.h; sourceTree = "<group>"; };
4E51B75D25E4115F0038575A /* DarwinNotificationCenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DarwinNotificationCenter.m; sourceTree = "<group>"; };
4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "JitsiMeetBroadcast Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
4EC49BB925BEDAC100E76218 /* SampleHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleHandler.h; sourceTree = "<group>"; };
4EC49BBA25BEDAC100E76218 /* SampleHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleHandler.m; sourceTree = "<group>"; };
4EC49BBC25BEDAC100E76218 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4EC49BC925BEDB6400E76218 /* SocketConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SocketConnection.h; sourceTree = "<group>"; };
4EC49BCA25BEDB6400E76218 /* SocketConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SocketConnection.m; sourceTree = "<group>"; };
4EC49BCF25BF19CF00E76218 /* SampleUploader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleUploader.h; sourceTree = "<group>"; };
4EC49BD025BF19CF00E76218 /* SampleUploader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleUploader.m; sourceTree = "<group>"; };
4EC49BDB25BF280A00E76218 /* extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = extension.entitlements; 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>"; };
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; };
@@ -189,14 +153,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
4EC49BB325BEDAC100E76218 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4EC49BB725BEDAC100E76218 /* ReplayKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -209,7 +165,6 @@
DEFDBBDB25656E3B00344B23 /* WebRTC.xcframework */,
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */,
D878B07B3FBD6E305EAA6B27 /* libPods-JitsiMeet.a */,
4EC49B8625BED71300E76218 /* ReplayKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -261,24 +216,6 @@
path = src;
sourceTree = "<group>";
};
4EC49BB825BEDAC100E76218 /* JitsiMeetBroadcast Extension */ = {
isa = PBXGroup;
children = (
4EC49BDB25BF280A00E76218 /* extension.entitlements */,
4EC49BB925BEDAC100E76218 /* SampleHandler.h */,
4EC49BBA25BEDAC100E76218 /* SampleHandler.m */,
4EC49BC925BEDB6400E76218 /* SocketConnection.h */,
4EC49BCA25BEDB6400E76218 /* SocketConnection.m */,
4EC49BCF25BF19CF00E76218 /* SampleUploader.h */,
4EC49BD025BF19CF00E76218 /* SampleUploader.m */,
4EC49BBC25BEDAC100E76218 /* Info.plist */,
4E51B75C25E4115F0038575A /* DarwinNotificationCenter.h */,
4E51B75D25E4115F0038575A /* DarwinNotificationCenter.m */,
);
name = "JitsiMeetBroadcast Extension";
path = "broadcast-extension";
sourceTree = "<group>";
};
5E96ADD5E49F3B3822EF9A52 /* Pods */ = {
isa = PBXGroup;
children = (
@@ -299,7 +236,6 @@
13B07FAE1A68108700A75B9A /* src */,
5E96ADD5E49F3B3822EF9A52 /* Pods */,
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
4EC49BB825BEDAC100E76218 /* JitsiMeetBroadcast Extension */,
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
);
indentWidth = 2;
@@ -312,7 +248,6 @@
13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -370,36 +305,17 @@
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
4EC49B9025BED71300E76218 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
4EC49BBE25BEDAC100E76218 /* PBXTargetDependency */,
);
name = JitsiMeet;
productName = "Jitsi Meet";
productReference = 13B07F961A680F5B00A75B9A /* jitsi-meet.app */;
productType = "com.apple.product-type.application";
};
4EC49BB525BEDAC100E76218 /* JitsiMeetBroadcast Extension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4EC49BC025BEDAC100E76218 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcast Extension" */;
buildPhases = (
4EC49BB225BEDAC100E76218 /* Sources */,
4EC49BB325BEDAC100E76218 /* Frameworks */,
4EC49BB425BEDAC100E76218 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "JitsiMeetBroadcast Extension";
productName = "JitsiMeetBroadcast Extension";
productReference = 4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -420,6 +336,8 @@
ProvisioningStyle = Automatic;
};
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = FC967L3QRG;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.SafariKeychain = {
enabled = 1;
@@ -429,9 +347,6 @@
};
};
};
4EC49BB525BEDAC100E76218 = {
CreatedOnToolsVersion = 12.2;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */;
@@ -450,7 +365,6 @@
13B07F861A680F5B00A75B9A /* JitsiMeet */,
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
4EC49BB525BEDAC100E76218 /* JitsiMeetBroadcast Extension */,
);
};
/* End PBXProject section */
@@ -483,13 +397,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
4EC49BB425BEDAC100E76218 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -625,17 +532,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
4EC49BB225BEDAC100E76218 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4EC49BCB25BEDB6400E76218 /* SocketConnection.m in Sources */,
4EC49BBB25BEDAC100E76218 /* SampleHandler.m in Sources */,
4E51B75E25E4115F0038575A /* DarwinNotificationCenter.m in Sources */,
4EC49BD125BF19CF00E76218 /* SampleUploader.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -649,11 +545,6 @@
target = 0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */;
targetProxy = 0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */;
};
4EC49BBE25BEDAC100E76218 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4EC49BB525BEDAC100E76218 /* JitsiMeetBroadcast Extension */;
targetProxy = 4EC49BBD25BEDAC100E76218 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -827,7 +718,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDebug;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = NO;
@@ -857,7 +748,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconRelease;
CODE_SIGN_ENTITLEMENTS = app.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = FC967L3QRG;
@@ -879,70 +770,6 @@
};
name = Release;
};
4EC49BC125BEDAC100E76218 /* 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 = "iPhone Developer";
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 = 11.0;
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;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
4EC49BC225BEDAC100E76218 /* 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 = "iPhone Developer";
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 = 11.0;
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;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1087,15 +914,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4EC49BC025BEDAC100E76218 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcast Extension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4EC49BC125BEDAC100E76218 /* Debug */,
4EC49BC225BEDAC100E76218 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -1,31 +0,0 @@
/*
* 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
extern NSNotificationName const kBroadcastStartedNotification;
extern NSNotificationName const kBroadcastStoppedNotification;
@interface DarwinNotificationCenter: NSObject
+ (instancetype)sharedInstance;
- (void)postNotificationWithName:(NSNotificationName)name;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,50 +0,0 @@
/*
* 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 "DarwinNotificationCenter.h"
NSNotificationName const kBroadcastStartedNotification = @"iOS_BroadcastStarted";
NSNotificationName const kBroadcastStoppedNotification = @"iOS_BroadcastStopped";
@implementation DarwinNotificationCenter {
CFNotificationCenterRef _notificationCenter;
}
+ (instancetype)sharedInstance {
static DarwinNotificationCenter *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
}
return self;
}
- (void)postNotificationWithName:(NSString*)name {
CFNotificationCenterPostNotification(_notificationCenter, (__bridge CFStringRef)name, NULL, NULL, true);
}
@end

View File

@@ -1,33 +0,0 @@
<?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>JitsiMeet 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.0.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>SampleHandler</string>
<key>RPBroadcastProcessMode</key>
<string>RPBroadcastProcessModeSampleBuffer</string>
</dict>
</dict>
</plist>

View File

@@ -1,21 +0,0 @@
/*
* 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/ReplayKit.h>
@interface SampleHandler : RPBroadcastSampleHandler
@end

View File

@@ -1,123 +0,0 @@
/*
* 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 "SampleHandler.h"
#import "SocketConnection.h"
#import "SampleUploader.h"
#import "DarwinNotificationCenter.h"
@interface SampleHandler ()
@property (nonatomic, retain) SocketConnection *clientConnection;
@property (nonatomic, retain) SampleUploader *uploader;
@end
@implementation SampleHandler
- (instancetype)init {
self = [super init];
if (self) {
self.clientConnection = [[SocketConnection alloc] initWithFilePath:self.socketFilePath];
[self setupConnection];
self.uploader = [[SampleUploader alloc] initWithConnection:self.clientConnection];
}
return self;
}
- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
NSLog(@"broadcast started");
[[DarwinNotificationCenter sharedInstance] postNotificationWithName:kBroadcastStartedNotification];
[self openConnection];
}
- (void)broadcastPaused {
// User has requested to pause the broadcast. Samples will stop being delivered.
}
- (void)broadcastResumed {
// User has requested to resume the broadcast. Samples delivery will resume.
}
- (void)broadcastFinished {
// User has requested to finish the broadcast.
[[DarwinNotificationCenter sharedInstance] postNotificationWithName:kBroadcastStoppedNotification];
[self.clientConnection close];
}
- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
static NSUInteger frameCount = 0;
switch (sampleBufferType) {
case RPSampleBufferTypeVideo:
// adjust frame rate by using every third frame
if (++frameCount%3 == 0 && self.uploader.isReady) {
[self.uploader sendSample:sampleBuffer];
}
break;
default:
break;
}
}
// MARK: Private Methods
- (NSString *)socketFilePath {
// the appGroupIdentifier must match the value provided in the app's info.plist for the RTCAppGroupIdentifier key
NSString *appGroupIdentifier = @"group.org.jitsi.meet.appgroup";
NSURL *sharedContainer = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupIdentifier];
NSString *socketFilePath = [[sharedContainer URLByAppendingPathComponent:@"rtc_SSFD"] path];
return socketFilePath;
}
- (void)setupConnection {
__weak __typeof(self) weakSelf = self;
self.clientConnection.didClose = ^(NSError *error) {
NSLog(@"client connection did close: %@", error);
if (error) {
[weakSelf finishBroadcastWithError:error];
}
else {
NSInteger JMScreenSharingStopped = 10001;
NSError *customError = [NSError errorWithDomain:RPRecordingErrorDomain
code:JMScreenSharingStopped
userInfo:@{NSLocalizedDescriptionKey: @"Screen sharing stopped"}];
[weakSelf finishBroadcastWithError:customError];
}
};
}
- (void)openConnection {
dispatch_queue_t queue = dispatch_queue_create("org.jitsi.meet.broadcast.connectTimer", 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 0.1 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
BOOL success = [self.clientConnection open];
if (success) {
dispatch_source_cancel(timer);
}
});
dispatch_resume(timer);
}
@end

View File

@@ -1,33 +0,0 @@
/*
* 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>
#import <ReplayKit/ReplayKit.h>
NS_ASSUME_NONNULL_BEGIN
@class SocketConnection;
@interface SampleUploader : NSObject
@property (nonatomic, assign, readonly) BOOL isReady;
- (instancetype)initWithConnection:(SocketConnection *)connection;
- (void)sendSample:(CMSampleBufferRef)sampleBuffer;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,155 +0,0 @@
/*
* 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 <MessageUI/MessageUI.h>
#import <ReplayKit/ReplayKit.h>
#import "SampleUploader.h"
#import "SocketConnection.h"
static const NSInteger kBufferMaxLenght = 10 * 1024;
@interface SampleUploader ()
@property (nonatomic, assign) BOOL isReady;
@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, strong) SocketConnection *connection;
@property (nonatomic, strong) CIContext *imageContext;
@property (nonatomic, strong) NSData *dataToSend;
@property (nonatomic, assign) NSUInteger byteIndex;
@end
@implementation SampleUploader
- (instancetype)initWithConnection:(SocketConnection *)connection {
self = [super init];
if (self) {
self.serialQueue = dispatch_queue_create("org.jitsi.meet.broadcast.sampleUploader", DISPATCH_QUEUE_SERIAL);
self.connection = connection;
[self setupConnection];
self.imageContext = [[CIContext alloc] initWithOptions:nil];
self.isReady = false;
}
return self;
}
- (void)sendSample:(CMSampleBufferRef)sampleBuffer {
self.isReady = false;
self.dataToSend = [self prepareSample:sampleBuffer];
self.byteIndex = 0;
dispatch_async(self.serialQueue, ^{
[self sendData];
});
}
// MARK: Private Methods
- (void)setupConnection {
__weak __typeof(self) weakSelf = self;
self.connection.didOpen = ^{
weakSelf.isReady = true;
};
self.connection.streamHasSpaceAvailable = ^{
dispatch_async(weakSelf.serialQueue, ^{
weakSelf.isReady = ![weakSelf sendData];
});
};
}
/**
This function downscales and converts to jpeg the provided sample buffer, then wraps the resulted image data into a CFHTTPMessageRef. Returns the serialized CFHTTPMessageRef.
*/
- (NSData *)prepareSample:(CMSampleBufferRef)sampleBuffer {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
CGFloat scaleFactor = 2;
size_t width = CVPixelBufferGetWidth(imageBuffer)/scaleFactor;
size_t height = CVPixelBufferGetHeight(imageBuffer)/scaleFactor;
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1/scaleFactor, 1/scaleFactor);
NSData *bufferData = [self jpegDataFromPixelBuffer:imageBuffer withScaling:scaleTransform];
CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
if (bufferData) {
CFHTTPMessageRef httpResponse = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Content-Length", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", bufferData.length]);
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Width", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", width]);
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Height", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", height]);
CFHTTPMessageSetBody(httpResponse, (__bridge CFDataRef)bufferData);
CFDataRef serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse);
CFRelease(httpResponse);
return CFBridgingRelease(serializedMessage);
}
return nil;
}
- (BOOL)sendData {
if (!self.dataToSend) {
NSLog(@"no data to send");
return false;
}
NSUInteger bytesLeft = self.dataToSend.length - self.byteIndex;
NSInteger length = bytesLeft > kBufferMaxLenght ? kBufferMaxLenght : bytesLeft;
uint8_t buffer[length];
[self.dataToSend getBytes:&buffer range:NSMakeRange(self.byteIndex, length)];
length = [self.connection writeBufferToStream:buffer maxLength:length];
if (length > 0) {
self.byteIndex += length;
bytesLeft -= length;
if (bytesLeft == 0) {
NSLog(@"video sample processed successfully");
self.dataToSend = nil;
self.byteIndex = 0;
}
}
else {
NSLog(@"writeBufferToStream failure");
}
return true;
}
- (NSData *)jpegDataFromPixelBuffer:(CVPixelBufferRef)pixelBuffer withScaling:(CGAffineTransform)scaleTransform {
CIImage *image = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer];
image = [image imageByApplyingTransform:scaleTransform];
NSDictionary *options = @{(NSString *)kCGImageDestinationLossyCompressionQuality: [NSNumber numberWithFloat:1.0]};
NSData *imageData = [self.imageContext JPEGRepresentationOfImage:image
colorSpace:image.colorSpace
options:options];
return imageData;
}
@end

View File

@@ -1,34 +0,0 @@
/*
* 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 SocketConnection : NSObject
@property (nonatomic, copy, nullable) void (^didOpen)(void);
@property (nonatomic, copy, nullable) void (^didClose)(NSError*);
@property (nonatomic, copy, nullable) void (^streamHasSpaceAvailable)(void);
- (instancetype)initWithFilePath:(nonnull NSString *)filePath;
- (BOOL)open;
- (void)close;
- (NSInteger)writeBufferToStream:(const uint8_t*)buffer maxLength:(NSInteger)length;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,189 +0,0 @@
/*
* 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.
*/
#include <sys/socket.h>
#include <sys/un.h>
#import "SocketConnection.h"
@interface SocketConnection () <NSStreamDelegate>
@property (nonatomic, copy) NSString *filePath;
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, strong) NSThread *networkThread;
@end
@implementation SocketConnection {
int _socket;
struct sockaddr_un _socketAddr;
}
- (instancetype)initWithFilePath:(NSString *)path {
self = [super init];
if (self) {
self.filePath = path;
[self setupSocketWithFilePath:path];
[self setupNetworkThread];
}
return self;
}
- (BOOL)open {
NSLog(@"Open socket connection");
if (![[NSFileManager defaultManager] fileExistsAtPath:self.filePath]) {
NSLog(@"failure: socket file missing");
return false;
}
int status = connect(_socket, (struct sockaddr *)&_socketAddr, sizeof(_socketAddr));
if (status < 0) {
NSLog(@"failure: socket connect (%d)", status);
return false;
}
[self.networkThread start];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, _socket, &readStream, &writeStream);
self.inputStream = (__bridge_transfer NSInputStream *)readStream;
self.inputStream.delegate = self;
[self.inputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;
self.outputStream.delegate = self;
[self.outputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
[self performSelector:@selector(scheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:true];
[self.inputStream open];
[self.outputStream open];
NSLog(@"read stream status: %ld", CFReadStreamGetStatus(readStream));
NSLog(@"write stream status: %ld", CFWriteStreamGetStatus(writeStream));
return true;
}
- (void)close {
[self performSelector:@selector(unscheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:true];
self.inputStream.delegate = nil;
self.outputStream.delegate = nil;
[self.inputStream close];
[self.outputStream close];
[self.networkThread cancel];
}
- (NSInteger)writeBufferToStream:(const uint8_t*)buffer maxLength:(NSInteger)length {
return [self.outputStream write:buffer maxLength:length];
}
// MARK: Private Methods
- (BOOL)isOpen {
return self.inputStream.streamStatus == NSStreamStatusOpen && self.outputStream.streamStatus == NSStreamStatusOpen;
}
- (void)setupSocketWithFilePath:(NSString*)path {
_socket = socket(AF_UNIX, SOCK_STREAM, 0);
memset(&_socketAddr, 0, sizeof(_socketAddr));
_socketAddr.sun_family = AF_UNIX;
strncpy(_socketAddr.sun_path, path.UTF8String, sizeof(_socketAddr.sun_path) - 1);
}
- (void)setupNetworkThread {
self.networkThread = [[NSThread alloc] initWithBlock:^{
do {
@autoreleasepool {
[[NSRunLoop currentRunLoop] run];
}
} while (![NSThread currentThread].isCancelled);
}];
self.networkThread.qualityOfService = NSQualityOfServiceUserInitiated;
}
- (void)scheduleStreams {
[self.inputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
[self.outputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
}
- (void)unscheduleStreams {
[self.inputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
[self.outputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
}
- (void)notifyDidClose:(NSError *)error {
if (self.didClose) {
self.didClose(error);
}
}
@end
#pragma mark - NSStreamDelegate
@implementation SocketConnection (NSStreamDelegate)
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventOpenCompleted:
NSLog(@"client stream open completed");
if (aStream == self.outputStream && self.didOpen) {
self.didOpen();
}
break;
case NSStreamEventHasBytesAvailable:
if (aStream == self.inputStream) {
uint8_t buffer;
NSInteger numberOfBytesRead = [(NSInputStream *)aStream read:&buffer maxLength:sizeof(buffer)];
if (!numberOfBytesRead && aStream.streamStatus == NSStreamStatusAtEnd) {
NSLog(@"server socket closed");
[self close];
[self notifyDidClose:nil];
}
}
break;
case NSStreamEventHasSpaceAvailable:
if (aStream == self.outputStream && self.streamHasSpaceAvailable) {
NSLog(@"client stream has space available");
self.streamHasSpaceAvailable();
}
break;
case NSStreamEventErrorOccurred:
NSLog(@"client stream error occurred: %@", aStream.streamError);
[self close];
[self notifyDidClose:aStream.streamError];
break;
default:
break;
}
}
@end

View File

@@ -1,10 +0,0 @@
<?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>

View File

@@ -51,7 +51,7 @@
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
NSLog(@"Enabling Firebase");
[FIRApp configure];
// Crashlytics defaults to disabled with the FirebaseCrashlyticsCollectionEnabled Info.plist key.
// Crashlytics defaults to disabled wirth the FirebaseCrashlyticsCollectionEnabled Info.plist key.
[[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:![jitsiMeet isCrashReportingDisabled]];
}

View File

@@ -45,8 +45,6 @@
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>FirebaseCrashlyticsCollectionEnabled</key>
<string>false</string>
<key>FirebaseScreenReportingEnabled</key>
<false/>
<key>ITSAppUsesNonExemptEncryption</key>
@@ -68,18 +66,14 @@
<string>See your scheduled meetings in the app.</string>
<key>NSCameraUsageDescription</key>
<string>Participate in meetings with video.</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>NSLocalNetworkUsageDescription</key>
<string>Local network is used for establishing Peer-to-Peer connections.</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>
@@ -105,5 +99,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>FirebaseCrashlyticsCollectionEnabled</key>
<string>false</string>
</dict>
</plist>

View File

@@ -35,7 +35,7 @@ fi
echo "PR_REPO_SLUG=${PR_REPO_SLUG} PR_BRANCH=${PR_BRANCH}"
# do the merge and git log
# do the marge and git log
if [ $PR_BRANCH != "master" ]; then
echo "Will merge ${PR_REPO_SLUG}/${PR_BRANCH} into master"

View File

@@ -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 hangs in the codesign step
# Fix for OS X Sierra that hungs 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"

View File

@@ -16,20 +16,6 @@ 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",

View File

@@ -24,8 +24,6 @@
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 */; };
@@ -87,8 +85,6 @@
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>"; };
@@ -235,8 +231,6 @@
C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */,
C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */,
C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */,
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */,
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */,
);
path = src;
sourceTree = "<group>";
@@ -304,7 +298,6 @@
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 */,
@@ -473,7 +466,6 @@
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 */,

View File

@@ -20,11 +20,11 @@
- (void)sendHangUp;
- (void)sendSetAudioMuted:(BOOL)muted;
- (void)sendEndpointTextMessage:(NSString*)message :(NSString*)to;
- (void)sendEndpointTextMessage:(NSString*)to :(NSString*)message;
- (void)toggleScreenShare;
- (void)retrieveParticipantsInfo:(void (^)(NSArray*))completion;
- (void)openChat:(NSString*)to;
- (void)closeChat;
- (void)sendChatMessage:(NSString*)message :(NSString*)to ;
- (void)sendChatMessage:(NSString*)to :(NSString*)message;
@end

View File

@@ -153,7 +153,7 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
[self sendEventWithName:setAudioMutedAction body:data];
}
- (void)sendEndpointTextMessage:(NSString*)message :(NSString*)to {
- (void)sendEndpointTextMessage:(NSString*)to :(NSString*)message {
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
data[@"to"] = to;
data[@"message"] = message;
@@ -185,7 +185,7 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
[self sendEventWithName:closeChatAction body:nil];
}
- (void)sendChatMessage:(NSString*)message :(NSString*)to {
- (void)sendChatMessage:(NSString*)to :(NSString*)message {
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
data[@"to"] = to;
data[@"message"] = message;

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.2.0</string>
<string>3.1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>

View File

@@ -23,7 +23,6 @@
#import "RCTBridgeWrapper.h"
#import "ReactUtils.h"
#import "RNSplashScreen.h"
#import "ScheenshareEventEmiter.h"
#import <RNGoogleSignin/RNGoogleSignin.h>
#import <WebRTC/RTCLogging.h>
@@ -32,7 +31,6 @@
@implementation JitsiMeet {
RCTBridgeWrapper *_bridgeWrapper;
NSDictionary *_launchOptions;
ScheenshareEventEmiter *_screenshareEventEmiter;
}
#pragma mak - This class is a singleton
@@ -52,9 +50,6 @@
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();

View File

@@ -38,11 +38,11 @@
- (void)leave;
- (void)hangUp;
- (void)setAudioMuted:(BOOL)muted;
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
- (void)sendEndpointTextMessage:(NSString*)to :(NSString*)message;
- (void)toggleScreenShare;
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler;
- (void)openChat:(NSString * _Nullable)to;
- (void)retrieveParticipantsInfo:(void (^)(NSArray*))completionHandler;
- (void)openChat:(NSString*)to;
- (void)closeChat;
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
- (void)sendChatMessage:(NSString*)to :(NSString*)message;
@end

View File

@@ -125,9 +125,9 @@ static void initializeViewsMap() {
[externalAPI sendSetAudioMuted:muted];
}
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
- (void)sendEndpointTextMessage:(NSString*)to :(NSString*)message {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendEndpointTextMessage:message :to];
[externalAPI sendEndpointTextMessage:to :message];
}
- (void)toggleScreenShare {
@@ -135,7 +135,7 @@ static void initializeViewsMap() {
[externalAPI toggleScreenShare];
}
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
- (void)retrieveParticipantsInfo:(void (^)(NSArray*))completionHandler {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI retrieveParticipantsInfo:completionHandler];
}
@@ -150,9 +150,9 @@ static void initializeViewsMap() {
[externalAPI closeChat];
}
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
- (void)sendChatMessage:(NSString*)to :(NSString*)message {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI sendChatMessage:message :to];
[externalAPI sendChatMessage:to :message];
}
#pragma mark Private methods
@@ -184,7 +184,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 below introduces imperativeness
// source code. The workaround implemented bellow introduces imperativeness
// in React Component props by defining a unique value per invocation.
props[@"timestamp"] = @(mach_absolute_time());

View File

@@ -1,25 +0,0 @@
/*
* 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

View File

@@ -1,63 +0,0 @@
/*
* 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), broadcastToggleNotificationCallback, (__bridge CFStringRef)kBroadcastStartedNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
CFNotificationCenterAddObserver(_notificationCenter, (__bridge const void *)(self), broadcastToggleNotificationCallback, (__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 broadcastToggleNotificationCallback(CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo) {
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
[externalAPI toggleScreenShare];
}
@end

View File

@@ -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 provider itself (when the user presses
// method. If it was called by the provder 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.

View File

@@ -29,7 +29,7 @@ public protocol PiPViewCoordinatorDelegate: class {
/// when is presented in Picure in Picture mode.
public class PiPViewCoordinator {
/// Limits the boundaries of view position on screen when minimized
/// Limits the boundries of view position on screen when minimized
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
left: 5,
bottom: 5,

View File

@@ -60,7 +60,7 @@ fi
echo "PR_REPO_SLUG=${PR_REPO_SLUG} PR_BRANCH=${PR_BRANCH}"
# do the merge and git log
# do the marge and git log
if [ $PR_BRANCH != "master" ]; then
echo "Will merge ${PR_REPO_SLUG}/${PR_BRANCH} into master"

View File

@@ -1,7 +1,6 @@
{
"addPeople": {
"add": "Invitér",
"add": "Inviter",
"countryNotSupported": "Vi supporterer ikke dette land endnu.",
"countryReminder": "Ringer du til uden for USA? Benyt venligst landekode!",
"disabled": "Du kan ikke invitere deltagere.",
@@ -16,33 +15,33 @@
"searchPeople": "Søg efter personer",
"searchPeopleAndNumbers": "Søg efter personer eller tilføj deres telefonnummer",
"telephone": "Telefon: {{number}}",
"title": "Invitér personer til dette møde"
"title": "Inviter personer til dette møde"
},
"audioDevices": {
"bluetooth": "Bluetooth",
"headphones": "Høretelefoner",
"phone": "Telefon",
"speaker": "Højtaler",
"none": "Der er ikke nogen lydenheder tilgængelige"
"none": "Der er ikke nogen lyd enheder tilgængelige"
},
"audioOnly": {
"audioOnly": "Kun lyd"
},
"calendarSync": {
"addMeetingURL": "Tilføj et mødelink",
"confirmAddLink": "Ønsker du at tilføje et Jitsi link til denne aftale?",
"confirmAddLink": "Ønsker du at tilføj et Jitsi link til denne aftale?",
"error": {
"appConfiguration": "Kalenderintegration er ikke sat korrekt op.",
"generic": "Der er sket en fejl. Verificér venligst dine kalenderindstillinger eller prøv at genopfriske din kalender.",
"notSignedIn": "Der er sket en fejl under login for at hente kalenderaftalerne. Kontroller venligst dine kalenderindstillinger og forsøg at logge ind igen."
"appConfiguration": "Kalender integration er ikke sat korrekt op.",
"generic": "Der er sket en fejl. Verificer venligst dine kalenderindstilliinger eller prøv at genopfriske din kalender.",
"notSignedIn": "Der er sket en fejl under log ind for at hente kalenderaftalerne. Kontroller venligst dine kalenderindstilliinger og forsøg at logge ind igen."
},
"join": "Deltag",
"joinTooltip": "Deltag i mødet",
"nextMeeting": "Næste møde",
"noEvents": "Der er ikke nogen kommende aftaler i kalenderen.",
"ongoingMeeting": "Igangværende møde",
"permissionButton": "Åbn indstillinger",
"permissionMessage": "Kalendertilladelsen er nødvendig for at kunne se dine aftaler i appen.",
"nextMeeting": "næste møde",
"noEvents": "Der er ikke nogen kommande aftaler i kalenderen.",
"ongoingMeeting": "igangværende møde",
"permissionButton": "Åben indstillinger",
"permissionMessage": "Kalender tilladelsen er nødvendig for at kunne se dine aftaler i appen.",
"refresh": "Genopfrisk aftaler",
"today": "I dag"
},
@@ -58,7 +57,7 @@
},
"privateNotice": "Privat besked til {{recipient}}",
"title": "Chat",
"you": "Dig"
"you": "dig"
},
"chromeExtensionBanner": {
"installExtensionText": "Installér plugin for Google Kalender og Office 365 integration",
@@ -70,8 +69,8 @@
},
"connection": {
"ATTACHED": "Forbundet",
"AUTHENTICATING": "Godkender",
"AUTHFAIL": "Godkendelse lykkedes ikke",
"AUTHENTICATING": "Autoriserer",
"AUTHFAIL": "Autorisation lykkedes ikke",
"CONNECTED": "Forbundet",
"CONNECTING": "Forbinder",
"CONNFAIL": "Forbindelse kunne ikke oprettes",
@@ -97,7 +96,7 @@
"localport": "Lokal port:",
"localport_plural": "Lokale porte:",
"more": "Vis mere",
"packetloss": "Pakketab:",
"packetloss": "Pakke tab:",
"quality": {
"good": "God",
"inactive": "Inaktiv",
@@ -134,9 +133,9 @@
"defaultNickname": "ex. Jane Pink",
"deviceError": {
"cameraError": "Kunne ikke forbinde til dit kamera",
"cameraPermission": "Kameratilladelse mangler",
"cameraPermission": "Kamera tilladelse mangler",
"microphoneError": "Kunne ikke forbinde til din mikrofon",
"microphonePermission": "Mikrofontilladelse mangler"
"microphonePermission": "Mikrofon tilladelse mangler"
},
"deviceSelection": {
"noPermission": "Tilladelse ikke givet",
@@ -146,21 +145,21 @@
},
"dialog": {
"accessibilityLabel": {
"liveStreaming": "Livestream"
"liveStreaming": "Live Stream"
},
"allow": "Tillad",
"alreadySharedVideoMsg": "En anden deltager deler allerede en video. Denne konference tillader kun en delt video af gangen.",
"alreadySharedVideoTitle": "Det er kun muligt at dele én video ad gangen",
"applicationWindow": "Applikationsvindue",
"alreadySharedVideoTitle": "Det er kun muligt at dele en video af gangen",
"applicationWindow": "Applikations vindue",
"Back": "Tilbage",
"cameraConstraintFailedError": "Dit kamera lever ikke op til de nødvendige krav..",
"cameraNotFoundError": "Kamera kunne ikke findes.",
"cameraNotSendingData": "Vi kan ikke tilgå dit kamera. Kontrollér venligst om der er en anden applikation der gør brug af dit kamera, eller vælg et andet kamera og genindlæs siden.",
"cameraNotSendingData": "Vi kan ikke tilgå dit kamera. Kontroller venligst om der er en anden applikation der gør brug af dit kamera, eller vælg en andet kamera og genindlæs siden.",
"cameraNotSendingDataTitle": "Kan ikke tilgå kamera",
"cameraPermissionDeniedError": "Du har ikke givet tilladelse til at bruge dit kamera. Du kan stadig deltage i mødet, men de andre deltagere vil ikke kunne se dig. Gør brug af kameraknappen i adressebaren for at give tilladelse.",
"cameraUnknownError": "Kan ikke gøre brug af dit kamera, årsag ukendt.",
"cameraUnsupportedResolutionError": "Dit kamera understøtter ikke den nødvendige opløsning.",
"Cancel": "Annullér",
"cameraPermissionDeniedError": "Du har ikke givet tilladelse til at bruge dit kamera. Du kan stadig deltage i mødet men de andre deltagere vil ikke kunne se dig. Gør brug af kamera knappen i adressebaren for at give tilladelse.",
"cameraUnknownError": "Kan ikke gør brug af dit kamera, årsag ukendt.",
"cameraUnsupportedResolutionError": "Dit kamera supporterer ikke den nødvendige opløsning.",
"Cancel": "Afbryd",
"close": "Luk",
"conferenceDisconnectMsg": "Kontroller venligst din netværksforbindelse. Forbinder igen om {{seconds}} sekunder…",
"conferenceDisconnectTitle": "Din forbindelse er blevet afbrudt.",
@@ -173,31 +172,31 @@
"connectErrorWithMsg": "Det var ikke muligt at forbinde til mødet: {{msg}}",
"connecting": "Forbinder",
"contactSupport": "Kontakt support",
"copy": "Kopiér invitation",
"dismiss": "Afvis",
"copy": "Kopier invitation",
"dismiss": "Afbryd",
"displayNameRequired": "Navn/alias er påkrævet",
"done": "Gem",
"enterDisplayName": "Indtast venligst dit navn/alias",
"error": "Fejl",
"externalInstallationMsg": "Du skal installere vores skærmdelingsplugin.",
"externalInstallationMsg": "Du skal installerer vores skærmdelings plugin.",
"externalInstallationTitle": "Plugin skal bruges",
"goToStore": "Gå til webstore",
"gracefulShutdown": "Vores service er pt. under vedligeholdelse. Prøv igen senere.",
"gracefulShutdown": "Vores service er pt. under vedligeholdese. Forsøg venligst igen senere.",
"IamHost": "Jeg er vært",
"incorrectRoomLockPassword": "",
"incorrectPassword": "Forkert brugernavn eller adgangskode",
"inlineInstallationMsg": "Du skal installere vores skærmdelingsplugin.",
"inlineInstallExtension": "Installér nu",
"inlineInstallationMsg": "Du skal installerer vores skærmdelings plugin.",
"inlineInstallExtension": "Installer nu",
"internalError": "Der er opstået en fejl: {{error}}",
"internalErrorTitle": "Intern fejl",
"kickMessage": "Du er blevet afbrudt fra mødet!",
"kickParticipantButton": "Smid ud",
"kickParticipantDialog": "Er du sikker på at du vil smide denne deltager ud?",
"kickParticipantTitle": "Smid denne deltager ud?",
"kickTitle": "Smidt ud af mødet",
"liveStreaming": "Livestreaming",
"liveStreamingDisabledForGuestTooltip": "Gæster kan ikke starte en livestream.",
"liveStreamingDisabledTooltip": "Livestreaming er slået fra.",
"kickParticipantButton": "Afbryd",
"kickParticipantDialog": "Er du sikker på at du vil afbryde den deltager?",
"kickParticipantTitle": "Afbryd denne deltager?",
"kickTitle": "Afbrudt fra møde",
"liveStreaming": "Live Streaming",
"liveStreamingDisabledForGuestTooltip": "Gæster kan ikke starte en live stream.",
"liveStreamingDisabledTooltip": "Live streaming er slået fra.",
"lockMessage": "Kunne ikke sikre mødet.",
"lockRoom": "Tilføj adgangskode",
"lockTitle": "Sikring fejlet",
@@ -209,79 +208,79 @@
"micNotFoundError": "Mikrofon ikke fundet.",
"micNotSendingData": "Vi kunne ikke tilgå din mikrofon. Vælg en anden mikrofon under indstillinger eller genindlæs applikationen.",
"micNotSendingDataTitle": "Kunne ikke tilgå din mikrofon",
"micPermissionDeniedError": "Du har ikke givet tilladelse til at bruge din mikrofon. Du kan stadig deltage i mødet men de andre deltagere kan ikke høre dig. Benyt kameraknappen i adressebaren for at give tilladelse.",
"micPermissionDeniedError": "Du har ikke givet tilladelse til at bruge din mikrofon. Du kan stadig deltage i mødet men de andre deltagere kan ikke høre dig. Benyt kamera knappen i adressbaren for at give tilladelse.",
"micUnknownError": "Kan ikke tilgå mikrofon af ukendt årsag.",
"muteEveryoneElseDialog": "Er du sikker på at du vil slå lyden fra for alle andre end denne deltager? Du kan ikke tænde lyden igen, men de kan selv tænde til enhver tid.",
"muteEveryoneElseDialog": "Er du sikker på at du vil slå lyden fra for denne deltager? Du kan ikke tænde igen, men de kan selv tænde til enhver tid.",
"muteEveryoneElseTitle": "Slå lyd fra for alle undtagen {{whom}}?",
"muteEveryoneDialog": "Er du sikker på at du vil slå lyden fra for alle? Du kan ikke tænde lyden igen, men de kan selv tænde til enhver tid.",
"muteEveryoneDialog": "Er du sikker på at du vil slå lyden fra for denne deltager? Du kan ikke tænde igen, men de kan selv tænde til enhver tid.",
"muteEveryoneTitle": "Slå lyd fra for alle?",
"muteEveryoneSelf": "Dig selv",
"muteEveryoneStartMuted": "Lyden er slået fra for alle fra nu af",
"muteEveryoneSelf": "dig selv",
"muteEveryoneStartMuted": "Lyden er slpet fra for alle fra nu af",
"muteParticipantBody": "Du kan ikke tænde for deres mikrofon, men de kan selv tænde for deres mikrofon til enhver tid.",
"muteParticipantButton": "Slå lyd fra",
"muteParticipantDialog": "Er du sikker på at du vil slå lyden fra for denne deltager? Du kan ikke tænde igen, men de kan selv tænde til enhver tid.",
"muteParticipantTitle": "Slå lyden fra for denne deltager?",
"Ok": "OK",
"muteParticipantTitle": "Slå lyd fra for denne deltager?",
"Ok": "Ok",
"passwordLabel": "Adgangskode",
"passwordNotSupported": "Det er ikke understøttet at sætte en adgangskode for mødet",
"passwordNotSupported": "Sætte en adgangskode for mødet er ikke understøttet.",
"passwordNotSupportedTitle": "Adgangskode er ikke understøttet",
"passwordRequired": "Adgangskode påkrævet",
"popupError": "Din browser blokerer for pop op-vinduer fra denne hjemmeside. Slå venligst pop op-vinduer til i din browsers indstillinger og forsøg igen.",
"popupErrorTitle": "Pop op-vinduer er blokeret",
"popupError": "Din browser blockerer for pop-upper fra denne hjemmeside. Slå venligst pop-upper til i din browsers indstillinger og forsøg igen.",
"popupErrorTitle": "Pop-up blokkeret",
"recording": "Optager",
"recordingDisabledForGuestTooltip": "Gæster kan ikke starte en optagelse",
"recordingDisabledTooltip": "Optagefunktionalitet er slået fra",
"recordingDisabledForGuestTooltip": "Gæster kan ikke starte en optagelse.",
"recordingDisabledTooltip": "Start optagelse er slået fra.",
"rejoinNow": "Deltag igen",
"remoteControlAllowedMessage": "{{user}} accepterede din anmodning om fjernstyring!",
"remoteControlDeniedMessage": "{{user}} afviste din anmodning om fjernstyring!",
"remoteControlAllowedMessage": "{{user}} accepterede din fjernstyring anmodning!",
"remoteControlDeniedMessage": "{{user}} afviste din fjernstyring anmodning!",
"remoteControlErrorMessage": "En fejl er opstået ved anmodning om fjernstyring for {{user}}!",
"remoteControlRequestMessage": "Vil du tillade at {{user}} fjerstyrer din computer?",
"remoteControlShareScreenWarning": "Bemærk at hvis du vælger \"Tillad\" så vil du tillade fjernstyring!",
"remoteControlStopMessage": "Fjernstyringssessionen er afsluttet!",
"remoteControlTitle": "Fjernstyring",
"remoteControlStopMessage": "Fjernstyrings sessionen er afsluttet!",
"remoteControlTitle": "Fjerstyring",
"Remove": "Fjern",
"removePassword": "Fjern adgangskode",
"removeSharedVideoMsg": "Er du sikker på at du vil fjerne din delte video?",
"removeSharedVideoMsg": "Er du sikker på at du vil fjerne di delte video?",
"removeSharedVideoTitle": "Fjern delt video",
"reservationError": "Reservation - system fejl",
"reservationErrorMsg": "Fejlkode: {{code}}, fejl: {{msg}}",
"retry": "Forsøg igen",
"screenSharingFailedToInstall": "Skærmdelings-plugin kunne ikke installeres.",
"screenSharingFailedToInstallTitle": "Skærmdelings-plugin kunne ikke installeres.",
"screenSharingFirefoxPermissionDeniedError": "Noget gik galt under skærmdeling. Kontrollér venligst at du har givet tilladelse til skærmdeling. ",
"screenSharingFirefoxPermissionDeniedError": "Noget gik galt under skærmdeling. Kontroller venligst at du har givet tilladelse til skærmdeling. ",
"screenSharingFirefoxPermissionDeniedTitle": "Skærmdeling kunne ikke startes!",
"screenSharingPermissionDeniedError": "Noget gik galt under skærmdeling. Kontrollér venligst at du har givet tilladelse til skærmdeling. Genindlæs siden og forsøg igen.",
"sendPrivateMessage": "Du har modtaget en privat besked. Ønsker du at svare privat eller til gruppen?",
"screenSharingPermissionDeniedError": "Noget gik galt under skærmdeling. Kontroller venligst at du har givet tilladelse til skærmdeling. Genindlæs siden og forsøg igen.",
"sendPrivateMessage": "Du har modtage en privat besked. Ønsker du at svare private eller øsnker du at svare til gruppen?",
"sendPrivateMessageCancel": "Send til gruppen",
"sendPrivateMessageOk": "Send privat",
"sendPrivateMessageTitle": "Send privat?",
"serviceUnavailable": "Service er ikke tilgængelig",
"sessTerminated": "Møde afsluttet",
"Share": "Del",
"shareVideoLinkError": "Angiv venligst et gyldigt YouTube link.",
"shareVideoLinkError": "Angiv venligst et validt Youtube link.",
"shareVideoTitle": "Del en video",
"shareYourScreen": "Del din skærm",
"shareYourScreenDisabled": "Skærmdeling er ikke slået til.",
"shareYourScreenDisabledForGuest": "Gæster kan ikke dele deres skærm.",
"startLiveStreaming": "Start livestream",
"startLiveStreaming": "Start live stream",
"startRecording": "Start optagelse",
"startRemoteControlErrorMessage": "Der er sket en fejl under opstart af fjernstyringssessionen!",
"stopLiveStreaming": "Stop livestream",
"startRemoteControlErrorMessage": "Der er sket en fejl under opstart af fjern kontrol sessionen!",
"stopLiveStreaming": "Stop live stream",
"stopRecording": "Stop optagelse",
"stopRecordingWarning": "Er du sikker på at du ønsker at stoppe optagelsen?",
"stopStreamingWarning": "Er du sikker på at du ønsker at stoppe livestreaming?",
"streamKey": "Livestream-nøgle",
"stopRecordingWarning": "Er du sikker på at du ønsker at stoppe optagelesen?",
"stopStreamingWarning": "Er du sikker på at du ønsker at stoppe live streaming?",
"streamKey": "Live stream nøgle",
"Submit": "Gem",
"thankYou": "Tak for at du har benyttet {{appName}}!",
"token": "token",
"tokenAuthFailed": "Beklager, du kan ikke deltage i dette møde.",
"tokenAuthFailedTitle": "Login fejlede",
"transcribing": "Transskribering",
"unlockRoom": "Fjern mødeadgangskode",
"userPassword": "Brugeradgangskode",
"WaitForHostMsg": "Mødet <b>{{room}}</b> er ikke startet endnu. Hvis du er værten, log venligst ind. Ellers vent på at værten kommer",
"WaitForHostMsgWOk": "Mødet <b>{{room}}</b> er ikke startet endnu. Hvis du er værten, tryk venligst på OK for at logge ind. Ellers vent på at værten kommer.",
"WaitingForHost": "Venter på vært …",
"tokenAuthFailedTitle": "Log ind fejlede",
"transcribing": "Transcribering",
"unlockRoom": "Fjern møde adgangskode",
"userPassword": "bruger adgangskode",
"WaitForHostMsg": "Mødet <b>{{room}}</b> er ikke startet endnu. Hvis du er hosten log venligst ind, ellers vent på at hosten kommer.",
"WaitForHostMsgWOk": "Mødet <b>{{room}}</b> er ikke startet endnu. Hvis du er hosten tryk venligst på OK for at logge ind, ellers vent på at hosten kommer.",
"WaitingForHost": "Venter på host …",
"Yes": "Ja",
"yourEntireScreen": "Hele din skærm"
},
@@ -296,37 +295,37 @@
"bad": "Dårlig",
"detailsLabel": "Uddyb.",
"good": "God",
"rateExperience": "Vurdér din oplevelse",
"rateExperience": "Vurder din oplevelse",
"veryBad": "Meget dårlig",
"veryGood": "Meget god"
},
"incomingCall": {
"answer": "Besvar",
"audioCallTitle": "Indende opkald",
"audioCallTitle": "Indkommende opkald",
"decline": "Afvis",
"productLabel": "Fra Appinux skærmbesøg",
"videoCallTitle": "Indende videoopkald"
"productLabel": "fra Appinux skærmbesøg",
"videoCallTitle": "Indkommende videoopkald"
},
"info": {
"accessibilityLabel": "Vis info",
"addPassword": "Tilføj adgangskode",
"cancelPassword": "Annullér adgangskode",
"cancelPassword": "Annulé adgangskode",
"conferenceURL": "Link:",
"country": "Land",
"dialANumber": "For at deltage i mødet ring til et af følgende telefonnumre og indtast pinkode.",
"dialInConferenceID": "Pinkode:",
"dialInNotSupported": "Deltagelse via telefonopkald er pt. ikke understøttet.",
"dialInNotSupported": "Deltagelse vis telfonen er pt. ikke understøttet.",
"dialInNumber": "Ring ind:",
"dialInSummaryError": "Der er opstået en fejl under indhenting af telefonopkaldsdetaljer. Forsøg venligst igen senere.",
"dialInSummaryError": "Der er opstået en fejl under hentning af ring ind information. Forsøg venligst igen senere.",
"dialInTollFree": "Gratis",
"genericError": "Der er opstået en fejl",
"inviteLiveStream": "For at se en livestream i dette møde klik på dette link: {{url}}",
"inviteLiveStream": "For at se en live stream i dette møde klik på dette link: {{url}}",
"invitePhone": "Ring-ind: {{number}},,{{conferenceID}}#\n",
"invitePhoneAlternatives": "",
"inviteURLFirstPartGeneral": "Der er blevet inviteret til at deltage i et møde.",
"inviteURLFirstPartPersonal": "{{name}} inviterer dig til at deltage i et møde.\n",
"inviteURLSecondPart": "\nDeltag i mødet:\n{{url}}\n",
"liveStreamURL": "Livestream:",
"liveStreamURL": "Live stream:",
"moreNumbers": "Flere telefonnumre",
"noNumbers": "Ring-ind nummer findes ikke.",
"noPassword": "Ingen",
@@ -334,8 +333,8 @@
"numbers": "Ring-ind numre",
"password": "Adgangskode:",
"title": "Del",
"tooltip": "Del link og ring-ind information for dette møde",
"label": "Mødeinformation"
"tooltip": "Del link og rind-ind information for dette møde",
"label": "Møde information"
},
"inviteDialog": {
"alertText": "Kunne ikke invitere nogle af deltagerne.",
@@ -349,54 +348,54 @@
"msg": "Der er opstået en fejl.",
"retry": "Forsøg igen",
"support": "Support",
"supportMsg": "Hvis denne fejl bliver ved, kontakt da support"
"supportMsg": "Hvis denne fejl bliver ved, kontakt"
},
"keyboardShortcuts": {
"focusLocal": "Sæt fokus din video",
"focusRemote": "Sæt fokus på en anden persons billede",
"focusLocal": "Fokusér på din video",
"focusRemote": "Fokusér på en anden persons video",
"fullScreen": "Vis eller annullér fuld skærm",
"keyboardShortcuts": "Tastaturgenveje",
"localRecording": "Vis eller skjul optagelseskontrolknapper",
"mute": "Slå din mikrofon til eller fra",
"localRecording": "Vis eller skjul optage kontrolknapper",
"mute": "Slå lyd fra eller til for din mikrofon",
"pushToTalk": "Tryk for at tale",
"raiseHand": "Løft eller sænk din hånd",
"showSpeakerStats": "Vis højtalerstatistik",
"toggleChat": "Åbn eller luk chat",
"toggleFilmstrip": "Vis eller skjul små videobilleder",
"toggleScreensharing": "Skift mellem video- og skærmdeling",
"raiseHand": "Lyft eller sænk din hånd",
"showSpeakerStats": "Vis højtaler statistik",
"toggleChat": "Åben eller luk chat",
"toggleFilmstrip": "Vis eller skjul video små billeder",
"toggleScreensharing": "Skift mellem video og skærmdeling",
"toggleShortcuts": "Vis eller skjul tastaturgenveje",
"videoMute": "Tænd eller sluk dit kamera",
"videoQuality": "Indstil videokvalitet"
"videoQuality": "Indstil opkaldskvalitet"
},
"liveStreaming": {
"busy": "Der arbejdes på at frigive streamingressourcer, forsøg venligst igen om et par minutter.",
"busyTitle": "Alle streams er optaget",
"busy": "Der arbejdes på at frigive streamingresourcer, forsøg venligst igen om et par minutter.",
"busyTitle": "Alle streamers er optaget",
"changeSignIn": "Skift konto.",
"choose": "Vælg en livestream",
"chooseCTA": "Vælg en stream-mulighed. Du er pt. logget ind som {{email}}.",
"enterStreamKey": "Indtast din livestream-nøgle til Youtube her.",
"error": "Fejl under livestreaming. Forsøg venligst igen.",
"choose": "Vælg en live stream",
"chooseCTA": "Vælg en stream mulighed. Du er pt. logget ind som {{email}}.",
"enterStreamKey": "Indtast din Youtube live stream her.",
"error": "Fejl under live streaming. Forsøg venligst igen.",
"errorAPI": "En fejl opstod ved forsøg på at tilgå din YouTube-broadcast. Forsøg venligst at logge ind igen.",
"errorLiveStreamNotEnabled": "Livestreaming er ikke slået til for {{email}}. Slå venligst livestreaming til eller log ind på en konto der har livestreaming slået til.",
"expandedOff": "Livestream er stoppet",
"errorLiveStreamNotEnabled": "Live Streaming er ikke slpet til for {{email}}. Slå venligst live streaming til eller log ind på en konto der har live streaming slået til.",
"expandedOff": "Live stream er stoppet",
"expandedOn": "Mødet bliver pt. streamet til Youtube.",
"expandedPending": "Livestreaming starter…",
"failedToStart": "Kunne ikke starte livestreaming",
"getStreamKeyManually": "Vi kunne ikke hente nogle livestreams. Forsøg at hente din livestream-nøgle fra YouTube.",
"invalidStreamKey": "Livestream-nøgle er muligvis forkert.",
"off": "Livestreaming er stoppet",
"offBy": "{{name}} stoppede livestreaming",
"on": "Livestreaming",
"onBy": "{{name}} startede livestreaming",
"pending": "Starter livestream…",
"serviceName": "Livestreaming-service",
"expandedPending": "Live streaming starter…",
"failedToStart": "Kunne ikke starte live streaming",
"getStreamKeyManually": "Vi kunne ikke hente nogle live streams. Forsøg at hente din live stream nøgle fra Youtube.",
"invalidStreamKey": "Live stream nøgle er muligvis forkert.",
"off": "Live Streaming er stoppet",
"offBy": "{{name}} stoppede live streaming",
"on": "Live Streaming",
"onBy": "{{name}} startede live streaming",
"pending": "Starter Live Stream…",
"serviceName": "Live Streaming service",
"signedInAs": "Du er pt. logget ind som:",
"signIn": "Log ind med Google",
"signInCTA": "Log ind eller indtast din livestream-nøgle fra YouTube.",
"signInCTA": "Log ind eller indtast din live stream nøgle fra YouTube.",
"signOut": "Log ud",
"start": "Start en livestream",
"start": "Start en live stream",
"streamIdHelp": "Hvad er dette?",
"unavailableTitle": "Livestreaming er ikke tilgængelig"
"unavailableTitle": "Live Streaming er ikke tilgængelig"
},
"localRecording": {
"clientState": {
@@ -404,24 +403,24 @@
"on": "Slået til",
"unknown": "Ukendt"
},
"dialogTitle": "Optagelseskontrolknapper",
"dialogTitle": "Optage kontrolknapper",
"duration": "Varighed",
"durationNA": "Ikke tilgængelig",
"encoding": "Encoding",
"label": "Optager",
"labelToolTip": "Optagelse er slået til",
"localRecording": "Lokal optagelse",
"labelToolTip": "Optager er slået til",
"localRecording": "Local Recording",
"me": "Mig",
"messages": {
"engaged": "Optagelse er slået til.",
"finished": "Optagelse {{token}} afsluttet. Send venligst den optagne fil til moderator.",
"finishedModerator": "Optagelse {{token}} afsluttet. Optagelsen er gemt. Bed deltageren om at sende deres optagelser.",
"engaged": "Optager er slået til.",
"finished": "Optagelses session {{token}} afsluttet. Send venligst den optagede fil til moderator.",
"finishedModerator": "Optagelses session {{token}} afsluttet. Optagelsen er gemt. Bed deltageren om at sende deres optagelser.",
"notModerator": "Du er ikke moderator. Du kan ikke starte eller stoppe optagelser."
},
"moderator": "Moderator",
"no": "Nej",
"participant": "Deltager",
"participantStats": "Deltagerstatistik",
"participantStats": "Deltager statistik",
"sessionToken": "Sessionsnøgle",
"start": "Start optagelse",
"stop": "Stop optagelse",
@@ -434,19 +433,19 @@
"connectedOneMember": "{{name}} deltager nu i mødet",
"connectedThreePlusMembers": "{{name}} og {{count}} andre deltager nu i mødet",
"connectedTwoMembers": "{{first}} og {{second}} deltager nu i mødet",
"disconnected": "Afbrudt",
"focus": "Konferencefokus",
"disconnected": "afbrudt",
"focus": "Konference fokus",
"focusFail": "{{component}} ikke tilgængelig - forsøg igen om {{ms}} sekunder",
"grantedTo": "Moderatorrettigheder er givet til {{to}}!",
"grantedTo": "Moderator rettigheder er givet til {{to}}!",
"invitedOneMember": "{{name}} er blevet inviteret",
"invitedThreePlusMembers": "{{name}} og {{count}} andre er blevet inviteret",
"invitedTwoMembers": "{{first}} og {{second}} er blevet inviteret",
"kickParticipant": "{{kicked}} blev afbrudt fra mødet af {{kicker}}",
"me": "Mig",
"moderator": "Moderatorrettigheder givet",
"moderator": "Moderator rettigheder givet!",
"muted": "Du har startet samtalen med lyden slået fra.",
"mutedTitle": "Din lyd er slået fra",
"mutedRemotelyTitle": "Din lyd er slået fra af {{participantDisplayName}}",
"mutedTitle": "Din lyd er slået fra!",
"mutedRemotelyTitle": "Din lyd er slået fra af {{participantDisplayName}}!",
"mutedRemotelyDescription": "",
"passwordRemovedRemotely": "",
"passwordSetRemotely": "",
@@ -454,16 +453,16 @@
"somebody": "Nogen",
"startSilentTitle": "",
"startSilentDescription": "",
"suboptimalExperienceDescription": "Hmmm... vi er bange for at din oplevelse med {{appName}} ikke vil være optimal. Vi arbejder på at forbedre dette, men indtil da forsøg venligst at bruge en af følgende <a href='{{recommendedBrowserPageLink}}' target='_blank'>fuldt understøttede internet browsere</a>.",
"suboptimalExperienceTitle": "Browser-advarsel",
"suboptimalExperienceDescription": "Hmmm... vi er bange for at din oplevelse med {{appName}} ikke vil være optimal. Vi arbejder på at forbedre dette, men indtil da forsøg venligst at bruge en af følgende <a href='{{recommendedBrowserPageLink}}' target='_blank'>fuldt understøttede internet browsere</a>.",
"suboptimalExperienceTitle": "Browser Advarsel",
"unmute": "",
"newDeviceCameraTitle": "Nyt kamera fundet",
"newDeviceAudioTitle": "Ny lydenhed fundet",
"newDeviceCameraTitle": "Nyt kamerat fundet",
"newDeviceAudioTitle": "Ny lyd enhed fundet",
"newDeviceAction": "Benyt"
},
"passwordSetRemotely": "Sat af et andet medlem",
"passwordSetRemotely": "sat af et andet medlem",
"passwordDigitsOnly": "Op til {{number}} tal",
"poweredby": "Powered by",
"poweredby": "powered by",
"presenceStatus": {
"busy": "Optaget",
"calling": "Ringer op…",
@@ -480,8 +479,8 @@
},
"profile": {
"setDisplayNameLabel": "Vælg navn/alias",
"setEmailInput": "Indtast email-adresse",
"setEmailLabel": "Indtast Gravatar-email-adresse",
"setEmailInput": "Indtast email",
"setEmailLabel": "Indtast Gravatar-e-post",
"title": "Profil"
},
"raisedHand": "Ønsker at tale",
@@ -489,13 +488,13 @@
"authDropboxText": "Upload til Dropbox",
"availableSpace": "Tilgængelig plads: {{spaceLeft}} MB (plads til ca. {{duration}} minutters optagelse)",
"beta": "BETA",
"busy": "Der arbejdes på at frigive optagelsesressourcer. Prøv venligst igen om nogle få minutter.",
"busyTitle": "Alle optagere er pt. optaget",
"busy": "Der arbejdes på at frigive optagelsesresourcer. Prøv venligst igen om nogle få minutter.",
"busyTitle": "Alle optagere er pt optaget",
"error": "Fejl under optagelse, forsøg venligst igen om lidt.",
"expandedOff": "Optagelse er stoppet",
"expandedOn": "Mødet bliver pt. optaget",
"expandedOff": "Optagelse er stoppet½",
"expandedOn": "Mødet bliver pt. optaget.",
"expandedPending": "Optagelse starter…",
"failedToStart": "Kunne ikke starte optagelse",
"failedToStart": "Kunne ikke start optagelse",
"fileSharingdescription": "Del optagelse med deltagere",
"live": "LIVE",
"loggedIn": "Logget ind som {{userName}}",
@@ -505,22 +504,22 @@
"onBy": "{{name}} startede optagelsen",
"pending": "Gør klar til at optage mødet…",
"rec": "REC",
"serviceDescription": "Din optagelse bliver gemt af optagelsesservicen",
"serviceName": "Optagelsesservice",
"serviceDescription": "Din optagelse bliver gemt af optager servicen",
"serviceName": "Optager service",
"signIn": "Log ind",
"signOut": "Log ud",
"unavailable": "Optagelsesservice {{serviceName}} er ikke tilgængelig. Der arbejdes på at løse problemet, forsøg igen senere.",
"unavailable": "Optager service {{serviceName}} er ikke tilgængelig. Der arbejdes på at løse problemet, forsøg igen senere.",
"unavailableTitle": "Optagelse ikke tilgængelig"
},
"sectionList": {
"pullToRefresh": "Træk ned for at opdatere"
"pullToRefresh": "Træk ned for update"
},
"settings": {
"calendar": {
"about": "{{appName}}s kalenderintegration benyttes til sikkert at tilgå din kalender så den kan læse fremtidige aftaler.",
"about": "{{appName}}s kalenderintegration benyttes til at sikkert tilgå din kalender så den kan læse fremtidige aftaler.",
"disconnect": "Afbryd",
"microsoftSignIn": "Log ind med Microsoft",
"signedIn": "Henter kalenderaftaler for {{email}}. Tryk på Afbryd for at afbryde forbindelsen.",
"signedIn": "Henter kalenderaftaler for {{email}}. Tryk på Afbryd knappen for at afbryde forbindelsen.",
"title": "Kalender"
},
"devices": "Enheder",
@@ -531,28 +530,28 @@
"more": "Mere",
"name": "Navn",
"noDevice": "Ingen",
"selectAudioOutput": "Lydudgang",
"selectAudioOutput": "Lyd udgang",
"selectCamera": "Kamera",
"selectMic": "Mikrofon",
"startAudioMuted": "Alle starter med lyden slået fra",
"startVideoMuted": "Alle starter med video slået fra",
"startVideoMuted": "Alle starter skjult",
"title": "Indstillinger"
},
"settingsView": {
"advanced": "Avanceret",
"alertOk": "OK",
"alertTitle": "Advarsel",
"alertURLText": "Den indtastede server-URL er ikke korrekt",
"alertURLText": "Den indtastede server url er ikke korrekt",
"buildInfoSection": "Build Information",
"conferenceSection": "Konference",
"disableCallIntegration": "Slå indbygget opkaldsintegration fra",
"disableCallIntegration": "Slå indbygget opkalds integration fra",
"disableP2P": "Slå Peer-To-Peer fra",
"displayName": "Navn/alias",
"email": "Email",
"header": "Indstillinger",
"profileSection": "Profil",
"serverURL": "Server-URL",
"showAdvanced": "Vis avancerede indstillinger",
"serverURL": "Server URL",
"showAdvanced": "Show advanced settings",
"startWithAudioMuted": "Start med lyden slået fra",
"startWithVideoMuted": "Start med video slået fra",
"version": "Version"
@@ -561,18 +560,18 @@
"dialInfoText": "\n\n=====\n\nØnsker du at ringe ind fra din telefon?\n\n{{defaultDialInNumber}}Klik på dette link for at se telefonnummeret for dette møde\n{{dialInfoPageUrl}}",
"mainText": "Klik på det følgende link for at deltage i mødet:\n{{roomUrl}}"
},
"speaker": "Deltager",
"speaker": "Højtaler",
"speakerStats": {
"hours": "{{count}}t",
"minutes": "{{count}}m",
"name": "Navn",
"seconds": "{{count}}s",
"speakerStats": "Deltagerstatistik",
"speakerTime": "Taletid"
"speakerStats": "Højtaler info",
"speakerTime": "Højtaler tid"
},
"startupoverlay": {
"policyText": " ",
"title": "{{app}} har brug for tilladelse til din mikrofon og dit kamera."
"title": "{{app}} har brug for adgang til din mikrofon og kamera."
},
"suspendedoverlay": {
"rejoinKeyTitle": "Forbind igen",
@@ -583,157 +582,157 @@
"accessibilityLabel": {
"audioOnly": "Tænd/sluk lyd",
"audioRoute": "Vælg lydenhed",
"callQuality": "Indstil opkaldskvalitet",
"cc": "Slå undertekster fra/til",
"callQuality": "Indstil møde kvalitet",
"cc": "Slå undertekst fra/til",
"chat": "Slå chat fra/til",
"document": "Slå delte dokumenter fra/til",
"download": "Hent vores apps",
"feedback": "Giv feedback",
"feedback": "Giv tilbagemelding",
"fullScreen": "Slå fuld skærm fra/til",
"hangup": "Forlad mødet",
"help": "Hjælp",
"invite": "Invitér deltagere",
"kick": "Smid deltager ud",
"localRecording": "Slå optagelseskontrol fra/til",
"invite": "Inviter deltagere",
"kick": "Afbryd deltager",
"localRecording": "Slå optagekontroller fra/til",
"lockRoom": "Slå mødeadgangskode fra/til",
"moreActions": "Slå \"Flere indstillinger\" menu fra/til",
"moreActionsMenu": "Flere indstillinger",
"moreOptions": "Vis flere indstillinger",
"moreActionsMenu": "Flere indtstillinger",
"moreOptions": "Show more options",
"mute": "Slå lyd til/fra",
"muteEveryone": "Slå lyd fra for alle",
"muteEveryone": "Mute everyone",
"pip": "Slå Billede-i-billede fra/til",
"privateMessage": "Send privatbesked",
"privateMessage": "Send privat besked",
"profile": "Redigér profil",
"raiseHand": "Slå løft hånden fra/til",
"recording": "Slå optagelse til/fra",
"remoteMute": "Slå lyd fra for deltager",
"Settings": "Slå indstillinger fra/til",
"sharedvideo": "Slå YouTube-videodeling fra/til",
"sharedvideo": "Slå YouTube video deling fra/til",
"shareRoom": "Invitér nogen",
"shareYourScreen": "Slå skærmdeling fra/til",
"shortcuts": "Slå genveje fra/til",
"show": "Vis",
"speakerStats": "Slå højtalerinfo fra/til",
"tileView": "Slå gittervisning fra/til",
"speakerStats": "Slå højtaler info fra/til",
"tileView": "Slå tileview fra/til",
"toggleCamera": "Slå kamera fra/til",
"videomute": "Slå video fra/til",
"videoblur": "Slå baggrundssløring fra/til"
"videoblur": "Slå baggrundsløring fra/til"
},
"addPeople": "Tilføj deltager til mødet",
"audioOnlyOff": "Slå kun-lyd møder fra",
"audioOnlyOn": "Slå kun-lyd møder til",
"audioRoute": "Vælg lydenhed",
"authenticate": "Log ind",
"callQuality": "Indstil opkaldskvalitet",
"chat": "Åbn/luk chat",
"callQuality": "Indstil møde kvalitet",
"chat": "Åben / Luk chat",
"closeChat": "Luk chat",
"documentClose": "Luk delt dokument",
"documentOpen": "Åben delt dokument",
"download": "Hent vores apps",
"enterFullScreen": "Vis fuld skærm",
"enterTileView": "Gittervisning",
"enterTileView": "Tileview",
"exitFullScreen": "Luk fuld skærm",
"exitTileView": "Luk gittervisning",
"feedback": "Giv feedback",
"exitTileView": "Luk Tileview",
"feedback": "Giv tilbagemeldign",
"hangup": "Forlad",
"help": "Hjælp",
"invite": "Invitér deltagere",
"login": "Log ind",
"logout": "Log ud",
"lowerYourHand": "Tag hånden ned",
"lowerYourHand": "Ta hånden ned",
"moreActions": "Flere handlinger",
"moreOptions": "Flere indstillinger",
"mute": "Slå lyd fra/til",
"mute": "Slå lyd fra / Slå lyd til",
"muteEveryone": "Slå lyd fra for alle",
"noAudioSignalTitle": "Der kommer ingen input fra din mikrofon",
"noAudioSignalTitle": "Der kommer ingen input fra din mikrofon!",
"noAudioSignalDesc": "Hvis du ikke med vilje har slået den fra under systemindstillinger eller hardware, kan du overveje at skifte enhed.",
"noAudioSignalDescSuggestion": "Hvis du ikke med vilje har slået den fra under systemindstillinger eller hardware, skal du overveje at skifte til den foreslåede enhed.",
"noAudioSignalDialInDesc": "Du kan også ringe op ved hjælp af:",
"noAudioSignalDialInLinkDesc": "Opkaldsnumre",
"noisyAudioInputTitle": "Det lyder som om din mikrofon laver støj",
"noisyAudioInputTitle": "Det lyder som om din mikrofon laver støj!",
"noisyAudioInputDesc": "Det lyder som om din mikrofon laver støj. Overvej venligst at slå lyden fra eller skifte enhed.",
"openChat": "Åbn chat",
"openChat": "Åben chat",
"pip": "Benyt Billede-i-billede",
"privateMessage": "Send privat besked",
"privateMessage": "Send private message",
"profile": "Rediger profil",
"raiseHand": "Ræk hånden op / Tag hånden ned",
"raiseYourHand": "Ræk hånden op",
"Settings": "Indstillinger",
"sharedvideo": "Del en YouTube-video",
"sharedvideo": "Del en Youtube video",
"shareRoom": "Invitér deltagere",
"shortcuts": "Vis genveje",
"speakerStats": "Deltagerstatistik",
"speakerStats": "Højtaler info",
"startScreenSharing": "Start skærmdeling",
"startSubtitles": "Vis undertekster",
"stopScreenSharing": "Stop skærmdeling",
"stopSubtitles": "Skjul undertekster",
"stopSharedVideo": "Stop YouTube-video",
"stopSharedVideo": "Stop YouTube video",
"talkWhileMutedPopup": "Forsøger du at sige noget? Din lyd er slået fra.",
"tileViewToggle": "Slå gittervisning fra/til",
"tileViewToggle": "Slå tileview fra/til",
"toggleCamera": "Slå kamera fra/til",
"videomute": "Start/stop kamera",
"videomute": "Start / Stop kamera",
"startvideoblur": "Slå baggrundssløring til",
"stopvideoblur": "Slå baggrundssløring fra"
},
"transcribing": {
"ccButtonTooltip": "Start/stop undertekster",
"error": "Transskription mislykkedes. Prøv venligst igen.",
"expandLabel": "Transskription er slået til",
"failedToStart": "Transskription kunne ikke starte",
"ccButtonTooltip": "Start / Stop undertekster",
"error": "Transkription mislykkedes. Prøv venligst igen.",
"expandLabel": "Transkription er i øjeblikket tændt",
"failedToStart": "Transkription kunne ikke starte",
"labelToolTip": "Mødet transkriberes",
"off": "Transkription stoppet",
"pending": "Forbereder sig til at transskribere mødet…",
"pending": "Forbereder sig til at transkribere mødet …",
"start": "Begynd at vise undertekster",
"stop": "Stop med at vise undertekster",
"stop": "Stop med at vist undertekster",
"tr": "TR"
},
"userMedia": {
"androidGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser",
"chromeGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser",
"edgeGrantPermissions": "Vælg <b><i>Ja</i> </b>, når din browser beder om tilladelser",
"electronGrantPermissions": "Giv tilladelse til at dit kamera og din mikrofon benyttes",
"firefoxGrantPermissions": "Vælg <b> <i> Del valgt enhed </i> </b>, når din browser beder om tilladelser",
"iexplorerGrantPermissions": "Vælg <b><i>OK</i> </b>, når din browser beder om tilladelser",
"nwjsGrantPermissions": "Giv tilladelse til at dit kamera og din mikrofon benyttes",
"operaGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser",
"react-nativeGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser",
"safariGrantPermissions": "Vælg <b><i>OK</i> </b>, når din browser beder om tilladelser"
"androidGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser.",
"chromeGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser.",
"edgeGrantPermissions": "Vælg <b><i>Ja</i> </b>, når din browser beder om tilladelser.",
"electronGrantPermissions": "Giv tilladelse til at bruge dit kamera og din mikrofon",
"firefoxGrantPermissions": "Vælg <b> <i> Del valgt enhed </i> </b>, når din browser beder om tilladelser.",
"iexplorerGrantPermissions": "Vælg <b><i>OK</i> </b>, når din browser beder om tilladelser.",
"nwjsGrantPermissions": "Giv tilladelse til at bruge dit kamera og din mikrofon",
"operaGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser.",
"react-nativeGrantPermissions": "Vælg <b><i> Tillad</i> </b>, når din browser beder om tilladelser.",
"safariGrantPermissions": "Vælg <b><i>OK</i> </b>, når din browser beder om tilladelser."
},
"videoSIPGW": {
"busy": "Vi arbejder på at frigøre ressourcer. Prøv igen om et par minutter",
"busy": "Vi arbejder på at frigøre ressourcer. Prøv igen om et par minutter.",
"busyTitle": "Mødeservice er i øjeblikket optaget",
"errorAlreadyInvited": "{{displayName}} allerede inviteret",
"errorInvite": "Mødet er endnu ikke etableret. Prøv igen senere",
"errorInviteFailed": "Vi arbejder på at løse problemet. Prøv igen senere",
"errorInviteFailedTitle": "Invitationen af {{displayName}} mislykkedes",
"errorInviteTitle": "Fejl ved invitation til møde",
"errorInvite": "Mødet er endnu ikke etableret. Prøv igen senere.",
"errorInviteFailed": "Vi arbejder på at løse problemet. Prøv igen senere.",
"errorInviteFailedTitle": "Inviterer {{displayName}} mislykkedes",
"errorInviteTitle": "Fejl ved indbydelse til møde",
"pending": "{{displayName}} er blevet inviteret"
},
"videoStatus": {
"audioOnly": "AUD",
"audioOnlyExpanded": "Du er i kun-lyd-tilstand. Denne tilstand sparer båndbredde, men du kan ikke se andres video",
"audioOnlyExpanded": "Du er i kun-lyd tilstand. Denne tilstand sparrer båndbredde, men du kan ikke se andres video.",
"callQuality": "Opkaldskvalitet",
"hd": "HD",
"hdTooltip": "Viser høj opløsning",
"hdTooltip": "Viser HD video",
"highDefinition": "Høj opløsning",
"labelTooiltipNoVideo": "Ingen video",
"labelTooltipAudioOnly": "Kun-lyd er slået til",
"labelTooltipAudioOnly": "Tilstand kun-lyd er slået til",
"ld": "LD",
"ldTooltip": "Viser lav opløsning",
"ldTooltip": "Viser lav opløsnings video",
"lowDefinition": "Lav opløsning",
"onlyAudioAvailable": "Kun-lyd er tilgængelig",
"onlyAudioSupported": "Vi understøtter kun-lyd i denne browser.",
"sd": "SD",
"sdTooltip": "Viser standard opløsning",
"sdTooltip": "Vider normal opløsnings video",
"standardDefinition": "Standard opløsning"
},
"videothumbnail": {
"domute": "Slå lyd fra",
"domuteOthers": "Slå lyd fra for alle andre",
"flip": "Flip",
"kick": "Smid ud",
"kick": "Afbryd",
"moderator": "Moderator",
"mute": "Deltagerens lyd er slået fra",
"mute": "Medlemmets lyd er slået fra",
"muted": "Lyd slået fra",
"remoteControl": "Fjernbetjening",
"show": "",
@@ -742,9 +741,9 @@
"welcomepage": {
"accessibilityLabel": {
"join": "Tryk for at deltage",
"roomname": "Skriv mødenavn"
"roomname": "Skriv møde navn"
},
"appDescription": "Chat med hele dit team eller invitér alle du kender. {{app}} er en fuldt krypteret, 100% open source videokonference løsning som du kan bruge hele dagen, hver dag, gratis - uden at du behøver en konto. ",
"appDescription": "Chat med hele dit team eller inviter alle du kender. {{app}} er en fuldt krypteret, 100% open source videokonference løsning som du kan bruge hele dagen, hver dag, gratis - uden at du behøver en konto. ",
"audioVideoSwitch": {
"audio": "Lyd",
"video": "Video"
@@ -756,13 +755,13 @@
"go": "Start",
"join": "Deltag",
"info": "Info",
"privacy": "Privacy",
"privacy": "Privatliv",
"recentList": "Seneste",
"recentListDelete": "Slet",
"recentListEmpty": "Din seneste liste er i øjeblikket tom. Chat med dit team, og du finder alle dine seneste møder her.",
"reducedUIText": "",
"roomname": "Indtast mødenavn",
"roomnameHint": "Indtast mødenavnet på det møde, du vil deltage i.",
"roomnameHint": "Indtast mødenavnet, du vil deltage i.",
"sendFeedback": "Giv tilbagemelding",
"betingelser": "Betingelser",
"title": "Skærmbesøg & videomøder"

View File

@@ -239,19 +239,12 @@
"muteEveryoneElseTitle": "Alle außer {{whom}} stummschalten?",
"muteEveryoneDialog": "Wollen Sie wirklich alle stummschalten? Sie können deren Stummschaltung nicht mehr beenden, aber sie können ihre Stummschaltung jederzeit selbst beenden.",
"muteEveryoneTitle": "Alle stummschalten?",
"muteEveryoneElsesVideoDialog": "Sobald die Kamera deaktiviert ist, können Sie sie nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"muteEveryoneElsesVideoTitle": "Die Kamera von allen außer {{whom}} ausschalten?",
"muteEveryonesVideoDialog": "Sind Sie sicher, dass Sie die Kamera von allen Teilnehmern deaktivieren möchten? Sie können sie nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"muteEveryonesVideoTitle": "Die Kamera von allen anderen ausschalten?",
"muteEveryoneSelf": "sich selbst",
"muteEveryoneStartMuted": "Alle beginnen von jetzt an stummgeschaltet",
"muteParticipantBody": "Sie können die Stummschaltung anderer Personen nicht aufheben, aber eine Person kann ihre eigene Stummschaltung jederzeit beenden.",
"muteParticipantButton": "Stummschalten",
"muteParticipantDialog": "Wollen Sie diese Person wirklich stummschalten? Sie können die Stummschaltung nicht wieder aufheben, die Person kann dies aber jederzeit selbst tun.",
"muteParticipantTitle": "Person stummschalten?",
"muteParticipantsVideoButton": "Kamera ausschalten",
"muteParticipantsVideoTitle": "Die Kamera von dieser Person ausschalten?",
"muteParticipantsVideoBody": "Sie können die Kamera nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
"Ok": "OK",
"passwordLabel": "Dieses Meeting wurde gesichert. Bitte geben Sie das $t(lockRoomPasswordUppercase) ein, um dem Meeting beizutreten.",
"passwordNotSupported": "Das Festlegen eines Konferenzpassworts wird nicht unterstützt.",
@@ -491,8 +484,6 @@
"mutedTitle": "Stummschaltung aktiv!",
"mutedRemotelyTitle": "Sie wurden von {{participantDisplayName}} stummgeschaltet!",
"mutedRemotelyDescription": "Sie können jederzeit die Stummschaltung aufheben, wenn Sie bereit sind zu sprechen. Wenn Sie fertig sind, können Sie sich wieder stummschalten, um Geräusche vom Meeting fernzuhalten.",
"videoMutedRemotelyTitle": "Ihre Kamera wurde von {{participantDisplayName}} ausgeschaltet!",
"videoMutedRemotelyDescription": "Sie können sie jederzeit wieder einschalten.",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) von einer anderen Person entfernt",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) von einer anderen Person gesetzt",
"raisedHand": "{{name}} möchte sprechen.",
@@ -691,7 +682,6 @@
},
"startupoverlay": {
"policyText": " ",
"genericTitle": "Die Konferenz muss Ihr Mikrofon und Ihre Kamera verwenden.",
"title": "{{app}} benötigt Kamera und Mikrofon."
},
"suspendedoverlay": {
@@ -724,16 +714,12 @@
"moreOptions": "Menü „Weitere Optionen“",
"mute": "„Audio stummschalten“ ein-/ausschalten",
"muteEveryone": "Alle stummschalten",
"muteEveryoneElse": "Alle anderen stummschalten",
"muteEveryonesVideo": "Alle Kameras ausschalten",
"muteEveryoneElsesVideo": "Alle anderen Kameras ausschalten",
"pip": "Bild-in-Bild-Modus ein-/ausschalten",
"privateMessage": "Private Nachricht senden",
"profile": "Profil bearbeiten",
"raiseHand": "„Melden“ ein-/ausschalten",
"recording": "Aufzeichnung ein-/ausschalten",
"remoteMute": "Personen stummschalten",
"remoteVideoMute": "Kamera von dieser Person ausschalten",
"security": "Sicherheitsoptionen",
"Settings": "Einstellungen ein-/ausschalten",
"sharedvideo": "YouTube-Videofreigabe ein-/ausschalten",
@@ -778,7 +764,6 @@
"moreOptions": "Weitere Optionen",
"mute": "Stummschaltung aktivieren / deaktivieren",
"muteEveryone": "Alle stummschalten",
"muteEveryonesVideo": "Alle Kameras ausschalten",
"noAudioSignalTitle": "Es kommt kein Input von Ihrem Mikrofon!",
"noAudioSignalDesc": "Wenn Sie das Gerät nicht absichtlich über die Systemeinstellungen oder die Hardware stumm geschaltet haben, sollten Sie einen Wechsel des Geräts in Erwägung ziehen.",
"noAudioSignalDescSuggestion": "Wenn Sie das Gerät nicht absichtlich über die Systemeinstellungen oder die Hardware stummgeschaltet haben, sollten Sie einen Wechsel auf das vorgeschlagene Gerät in Erwägung ziehen.",
@@ -864,16 +849,13 @@
},
"videothumbnail": {
"domute": "Stummschalten",
"domuteVideo": "Kamera ausschalten",
"domuteOthers": "Alle anderen stummschalten",
"domuteVideoOfOthers": "Alle anderen Kameras auschalten",
"flip": "Spiegeln",
"grantModerator": "Moderationsrechte vergeben",
"kick": "Hinauswerfen",
"moderator": "Moderation",
"mute": "Person ist stumm geschaltet",
"muted": "Stummgeschaltet",
"videoMuted": "Kamera ausgeschaltet",
"remoteControl": "Fernsteuerung",
"show": "Im Vordergrund anzeigen",
"videomute": "Person hat die Kamera angehalten"

View File

@@ -105,8 +105,8 @@
"bitrate": "Débit :",
"bridgeCount": "Nombre de serveurs :",
"codecs": "Codecs (A/V) :",
"connectedTo": "Connecté à :",
"e2e_rtt": "E2E RTT :",
"connectedTo": "Connecté à :",
"framerate": "Images par seconde :",
"less": "Cacher les détails",
"localaddress": "Adresse locale :",
@@ -251,7 +251,7 @@
"muteParticipantDialog": "Êtes-vous sûr(e) de vouloir couper le micro de ce participant ? Seul le participant pourra ensuite réactiver son micro à tout moment.",
"muteParticipantTitle": "Couper le micro de ce participant ?",
"Ok": "Ok",
"passwordLabel": "La réunion a été verrouillée par un(e) participant(e). Veuillez entrer le $t(lockRoomPassword) pour la rejoindre.",
"passwordLabel": "La réunion a été verrouillée par un·e participant·e. Veuillez entrer le $t(lockRoomPassword) pour la rejoindre.",
"passwordNotSupported": "La définition d'un $t(lockRoomPassword) de réunion n'est pas prise en charge.",
"passwordNotSupportedTitle": "L'ajout d'un $t(lockRoomPassword) n'est pas supporté",
"passwordRequired": "$t(lockRoomPasswordUppercase) requis",
@@ -396,16 +396,16 @@
"focusRemote": "Épingler la vidéo de quelqu'un d'autre",
"fullScreen": "Activer / Désactiver le mode plein écran",
"keyboardShortcuts": "Raccourcis clavier",
"localRecording": "Afficher / Masquer les commandes de l'enregistrement local",
"mute": "Activer / Désactiver le microphone",
"pushToTalk": "Maintenir la touche pour parler",
"raiseHand": "Lever / Baisser la main",
"localRecording": "Afficher ou masquer les commandes de l'enregistrement local",
"mute": "Activer ou désactiver le microphone",
"pushToTalk": "Appuyer pour parler",
"raiseHand": "Lever ou baisser la main",
"showSpeakerStats": "Afficher les statistiques de l'interlocuteur",
"toggleChat": "Ouvrir / Fermer le panneau de conversation",
"toggleFilmstrip": "Afficher / Masquer les vignettes vidéos",
"toggleChat": "Ouvrir ou fermer le panneau de conversation",
"toggleFilmstrip": "Afficher ou masquer les vignettes vidéos",
"toggleScreensharing": "Basculer entre la caméra et le partage d'écran",
"toggleShortcuts": "Afficher / Masquer les raccourcis clavier",
"videoMute": "Démarrer / Arrêter votre caméra",
"toggleShortcuts": "Afficher ou masquer les raccourcis clavier",
"videoMute": "Démarrer ou arrêter votre caméra",
"videoQuality": "Accorder la qualité des appels"
},
"liveStreaming": {
@@ -423,15 +423,15 @@
"expandedOff": "La diffusion en direct a été arrêtée",
"expandedOn": "La conférence est en cours de diffusion sur YouTube.",
"expandedPending": "La diffusion en direct a commencé ...",
"failedToStart": "La diffusion n'a pas réussi à démarrer",
"getStreamKeyManually": "Nous n'avons pas réussi à récupérer un flux de direct. Essayez d'obtenir votre clé de diffusion en direct sur YouTube.",
"failedToStart": "La diffusion n'as pas réussi à démarrer",
"getStreamKeyManually": "Nous n'avons pu récupérer aucun flux en direct. Essayez d'obtenir votre clé de diffusion en direct sur YouTube.",
"invalidStreamKey": "La clé de diffusion en direct n'est peut-être pas correcte.",
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre diffusion sera limitée à {{limit}} min. Pour un streaming illimité, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
"limitNotificationDescriptionWeb": "En raison de la forte demande, votre diffusion sera limitée à {{limit}} min. Pour un streaming illimité, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
"limitNotificationDescriptionNative": "Votre diffusion sera limitée à {{limit}} min. Pour un streaming illimité, essayez {{app}}.",
"off": "La diffusion en direct (streaming) a été arrêté",
"offBy": "{{name}} a arrêté la diffusion en direct",
"off": "Le Streaming a été arrêté",
"offBy": "{{name}} a arrêté la diffusion en continu",
"on": "En direct",
"onBy": "{{name}} a démarré la diffusion en direct",
"onBy": "{{name}} a démarré la diffusion en continu",
"pending": "Lancement du direct ...",
"serviceName": "Service de diffusion en direct",
"signedInAs": "Vous êtes connecté en tant que :",
@@ -461,12 +461,12 @@
"messages": {
"engaged": "L'enregistrement local a démarré.",
"finished": "L'enregistrement de la session {{token}} s'est terminé. Merci d'envoyer le fichier au modérateur.",
"finishedModerator": "L'enregistrement de la session {{token}} s'est terminé. L'enregistrement a bien été sauvegardée. Merci de demander aux autres participants de soumettre leurs enregistrements.",
"finishedModerator": "L'enregistrement de la session {{token}} s'est terminé. La piste a bien été sauvegardée. Merci de demander aux autres participants de soumettre leurs enregistrements.",
"notModerator": "Vous n'êtes pas le modérateur. Vous ne pouvez pas démarrer ou arrêter un enregistrement local."
},
"moderator": "Modérateur ",
"moderator": "Modérateur",
"no": "Non",
"participant": "Participant(e)",
"participant": "Participant·e",
"participantStats": "Statistiques du participant",
"sessionToken": "Token de la session",
"start": "Démarrer l'enregistrement",
@@ -484,7 +484,7 @@
"focus": "Focus de conférence",
"focusFail": "{{component}} n'est pas disponible - réessayez dans {{ms}} sec",
"grantedTo": "Droits modérateur accordés à {{to}} !",
"invitedOneMember": "{{name}} a été invité(e)",
"invitedOneMember": "{{name}} a été invité·e",
"invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités",
"invitedTwoMembers": "{{first}} et {{second}} ont été invités",
"kickParticipant": "{{kicked}} a été expulsé par {{kicker}}",
@@ -516,8 +516,7 @@
"poweredby": "produit par",
"prejoin": {
"audioAndVideoError": "Erreur audio et video:",
"audioDeviceProblem": "Il y a un problème avec votre périphérique audio",
"audioOnlyError": "Erreur audio :",
"audioOnlyError": "Erreur audio:",
"audioTrackError": "N'a pas pu créer la piste audio.",
"calling": "Appel",
"callMe": "Appelez-moi",
@@ -603,7 +602,7 @@
"expandedOff": "L'enregistrement a été arrêté",
"expandedOn": "Cette conférence est actuellement en cours d'enregistrement.",
"expandedPending": "Démarrage de l'enregistrement ...",
"failedToStart": "L'enregistrement n'a pas réussi à démarrer",
"failedToStart": "L'enregistrement n'as pas réussi à démarrer",
"fileSharingdescription": "Partager l'enregistrement avec les participants de la réunion",
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
"limitNotificationDescriptionNative": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <3> {{app}} </3>.",
@@ -703,48 +702,48 @@
},
"toolbar": {
"accessibilityLabel": {
"audioOnly": "Activer / Désactiver le mode voix uniquement",
"audioOnly": "Activer/désactiver le mode voix uniquement",
"audioRoute": "Sélectionner la source audio",
"callQuality": "Ajuster la qualité vidéo",
"cc": "Activer / Désactiver les sous-titres",
"chat": "Afficher / Masquer la discussion instantanée",
"document": "Activer / Désactiver le document partagé",
"cc": "Activer/désactiver les sous-titres",
"chat": "Afficher/masquer la discussion instantanée",
"document": "Activer/désactiver le document partagé",
"download": "Télécharger nos applications",
"embedMeeting": "Intégrer la réunion",
"feedback": "Laisser des commentaires",
"fullScreen": "Activer / Désactiver le plein écran",
"fullScreen": "Activer/désactiver le plein écran",
"grantModerator": "Nommer modérateur",
"hangup": "Quitter la conversation",
"help": "Aide",
"invite": "Inviter des participants",
"kick": "Expulser le participant",
"lobbyButton": "Activer / Désactiver le mode salle d'attente",
"localRecording": "Activer / Désactiver les contrôles d'enregistrement local",
"lockRoom": "Activer / Désactiver le mot de passe de la réunion",
"moreActions": "Activer / Désactiver le menu d'actions supplémentaires",
"lobbyButton": "Activer / désactiver le mode lobby",
"localRecording": "Activer/désactiver les contrôles d'enregistrement local",
"lockRoom": "Activer/Désactiver le mot de passe de la réunion",
"moreActions": "Activer/désactiver le menu d'actions supplémentaires",
"moreActionsMenu": "Menu d'actions supplémentaires",
"moreOptions": "Voir plus d'options",
"mute": "Activer / Désactiver l'audio",
"mute": "Activer/désactiver l'audio",
"muteEveryone": "Rendre muet tout le monde",
"pip": "Activer / Désactiver le mode Picture in Picture",
"pip": "Activer/désactiver le mode Picture in Picture",
"privateMessage": "Envoyer un message privé",
"profile": "Éditer votre profil",
"raiseHand": "Lever / Baisser la main",
"recording": "Activer / Désactiver l'enregistrement",
"raiseHand": "Lever/baisser la main",
"recording": "Activer/désactiver l'enregistrement",
"remoteMute": "Désactiver le micro du participant",
"security": "Options de sécurité",
"Settings": "Afficher / Masquer le menu des paramètres",
"sharedvideo": "Démarrer / Arrêter le partage de vidéo YouTube",
"Settings": "Afficher/masquer le menu des paramètres",
"sharedvideo": "Démarrer/arrêter le partage de vidéo YouTube",
"shareRoom": "Inviter quelqu'un",
"shareYourScreen": "Activer / Désactiver le partage d'écran",
"shortcuts": "Afficher / Masquer les raccourcis",
"shareYourScreen": "Activer/désactiver le partage d'écran",
"shortcuts": "Afficher/masquer les raccourcis",
"show": "Afficher en premier plan",
"speakerStats": "Afficher / Cacher les statistiques de parole",
"tileView": "Activer / Désactiver la vue mosaïque",
"speakerStats": "Afficher/cacher les statistiques de parole",
"tileView": "Activer/désactiver la vue mosaïque",
"toggleCamera": "Changer de caméra",
"toggleFilmstrip": "Basculer de pellicule",
"videomute": "Activer / Désactiver la vidéo",
"videoblur": "Activer / Désactiver le flou de la vidéo"
"videomute": "Activer/désactiver la vidéo",
"videoblur": "Activer/désactiver le flou de la vidéo"
},
"addPeople": "Ajouter des personnes à votre appel",
"audioOnlyOff": "Désactiver le mode bande passante réduite",
@@ -767,8 +766,8 @@
"hangup": "Quitter",
"help": "Aide",
"invite": "Inviter des participants",
"lobbyButtonDisable": "Désactiver le mode salle d'attente / contrôle des participant(e)s",
"lobbyButtonEnable": "Activer le mode salle d'attente / contrôle des participant(e)s",
"lobbyButtonDisable": "Désactiver le contrôle des participant·e·s",
"lobbyButtonEnable": "Activer le contrôle des participant·e·s",
"login": "Connexion",
"logout": "Déconnexion",
"lowerYourHand": "Baisser la main",
@@ -801,22 +800,22 @@
"stopSubtitles": "Désactiver les sous-titres",
"stopSharedVideo": "Arrêter la vidéo YouTube",
"talkWhileMutedPopup": "Vous voulez parler ? Votre micro est coupé.",
"tileViewToggle": "Activer / Désactiver la vue mosaïque",
"tileViewToggle": "Activer/désactiver la vue mosaïque",
"toggleCamera": "Changer de caméra",
"videomute": "Démarrer / Arrêter la caméra",
"startvideoblur": "Flouter mon arrière plan",
"stopvideoblur": "Désactiver le flou d'arrière-plan"
},
"transcribing": {
"ccButtonTooltip": "Activer / Désactiver les sous-titres",
"ccButtonTooltip": "Activer/Désactiver les sous-titres",
"error": "Échec de la transcription. Veuillez réessayer.",
"expandedLabel": "La transcription est actuellement activée",
"failedToStart": "Échec de démarrage de la transcription",
"labelToolTip": "La transcription de la réunion est en cours",
"off": "La transcription désactivée",
"pending": "Préparation de la transcription de la réunion ...",
"start": "Activer les sous-titres",
"stop": "Désactiver les sous-titres",
"start": "Afficher/masquer les sous-titres",
"stop": "Désactiver le sous-titrage",
"tr": "TR"
},
"userMedia": {
@@ -940,10 +939,10 @@
"nameField": "Saisissez votre nom",
"notificationLobbyAccessDenied": "{{targetParticipantName}} a été refusé par {{originParticipantName}}",
"notificationLobbyAccessGranted": "{{targetParticipantName}} a été accepté par {{originParticipantName}}",
"notificationLobbyDisabled": "Le mode salle d'attente a été désactivé par {{originParticipantName}}",
"notificationLobbyEnabled": "Le mode salle d'attente a été activé par {{originParticipantName}}",
"notificationTitle": "Salle d'attente",
"passwordField": "Veuillez saisir le mot de passe de la réunion",
"notificationLobbyDisabled": "Lobby a été désactivé par {{originParticipantName}}",
"notificationLobbyEnabled": "Lobby a été activé par {{originParticipantName}}",
"notificationTitle": "Lobby",
"passwordField": "Saisissez le mot de passe de la réunion",
"passwordJoinButton": "Rejoindre",
"reject": "Refuser",
"toggleLabel": "Activer la salle d'attente"

View File

@@ -61,7 +61,6 @@
"today": "Today"
},
"chat": {
"enter": "Enter chat room",
"error": "Error: your message was not sent. Reason: {{error}}",
"fieldPlaceHolder": "Type your message here",
"messagebox": "Type a message",
@@ -241,19 +240,12 @@
"muteEveryoneElseTitle": "Mute everyone except {{whom}}?",
"muteEveryoneDialog": "Are you sure you want to mute everyone? You won't be able to unmute them, but they can unmute themselves at any time.",
"muteEveryoneTitle": "Mute everyone?",
"muteEveryoneElsesVideoDialog": "Once the camera is disabled, you won't be able to turn it back on, but they can turn it back on at any time.",
"muteEveryoneElsesVideoTitle": "Disable everyone's camera except {{whom}}?",
"muteEveryonesVideoDialog": "Are you sure you want to disable everyone's camera? You won't be able to turn it back on, but they can turn it back on at any time.",
"muteEveryonesVideoTitle": "Disable everyone's camera?",
"muteEveryoneSelf": "yourself",
"muteEveryoneStartMuted": "Everyone starts muted from now on",
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
"muteParticipantButton": "Mute",
"muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
"muteParticipantTitle": "Mute this participant?",
"muteParticipantsVideoButton": "Disable camera",
"muteParticipantsVideoTitle": "Disable camera of this participant?",
"muteParticipantsVideoBody": "You won't be able to turn the camera back on, but they can turn it back on at any time.",
"Ok": "OK",
"passwordLabel": "The meeting has been locked by a participant. Please enter the $t(lockRoomPassword) to join.",
"passwordNotSupported": "Setting a meeting $t(lockRoomPassword) is not supported.",
@@ -313,7 +305,6 @@
"unlockRoom": "Remove meeting $t(lockRoomPassword)",
"user": "user",
"userPassword": "user password",
"videoLink": "Video link",
"WaitForHostMsg": "The conference <b>{{room}}</b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
"WaitForHostMsgWOk": "The conference <b>{{room}}</b> has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.",
"WaitingForHost": "Waiting for the host ...",
@@ -332,11 +323,6 @@
"embedMeeting": {
"title": "Embed this meeting"
},
"virtualBackground": {
"title": "Backgrounds",
"enableBlur": "Enable blur",
"removeBackground": "Remove background"
},
"feedback": {
"average": "Average",
"bad": "Bad",
@@ -497,8 +483,6 @@
"mutedTitle": "You're muted!",
"mutedRemotelyTitle": "You have been muted by {{participantDisplayName}}!",
"mutedRemotelyDescription": "You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.",
"videoMutedRemotelyTitle": "Your camera has been disabled by {{participantDisplayName}}!",
"videoMutedRemotelyDescription": "You can always turn it on again.",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
"raisedHand": "{{name}} would like to speak.",
@@ -731,19 +715,15 @@
"moreOptions": "Show more options",
"mute": "Toggle mute audio",
"muteEveryone": "Mute everyone",
"muteEveryoneElse": "Mute everyone else",
"muteEveryonesVideo": "Disable everyone's camera",
"muteEveryoneElsesVideo": "Disable everyone else's camera",
"pip": "Toggle Picture-in-Picture mode",
"privateMessage": "Send private message",
"profile": "Edit your profile",
"raiseHand": "Toggle raise hand",
"recording": "Toggle recording",
"remoteMute": "Mute participant",
"remoteVideoMute": "Disable camera of participant",
"security": "Security options",
"Settings": "Toggle settings",
"sharedvideo": "Toggle YouTube video sharing",
"sharedvideo": "Toggle Youtube video sharing",
"shareRoom": "Invite someone",
"shareYourScreen": "Toggle screenshare",
"shortcuts": "Toggle shortcuts",
@@ -753,10 +733,9 @@
"toggleCamera": "Toggle camera",
"toggleFilmstrip": "Toggle filmstrip",
"videomute": "Toggle mute video",
"selectBackground": "Select Background"
"videoblur": "Toggle video blur"
},
"addPeople": "Add people to your call",
"audioSettings": "Audio settings",
"audioOnlyOff": "Disable low bandwidth mode",
"audioOnlyOn": "Enable low bandwidth mode",
"audioRoute": "Select the sound device",
@@ -786,7 +765,6 @@
"moreOptions": "More options",
"mute": "Mute / Unmute",
"muteEveryone": "Mute everyone",
"muteEveryonesVideo": "Disable everyone's camera",
"noAudioSignalTitle": "There is no input coming from your mic!",
"noAudioSignalDesc": "If you did not purposely mute it from system settings or hardware, consider switching the device.",
"noAudioSignalDescSuggestion": "If you did not purposely mute it from system settings or hardware, consider switching to the suggested device.",
@@ -815,7 +793,8 @@
"tileViewToggle": "Toggle tile view",
"toggleCamera": "Toggle camera",
"videomute": "Start / Stop camera",
"selectBackground": "Select background"
"startvideoblur": "Blur my background",
"stopvideoblur": "Disable background blur"
},
"transcribing": {
"ccButtonTooltip": "Start / Stop subtitles",
@@ -870,16 +849,13 @@
"videothumbnail": {
"connectionInfo": "Connection Info",
"domute": "Mute",
"domuteVideo": "Disable camera",
"domuteOthers": "Mute everyone else",
"domuteVideoOfOthers": "Disable camera of everyone else",
"flip": "Flip",
"grantModerator": "Grant Moderator",
"kick": "Kick out",
"moderator": "Moderator",
"mute": "Participant is muted",
"muted": "Muted",
"videoMuted": "Camera disabled",
"remoteControl": "Start / Stop remote control",
"show": "Show on stage",
"videomute": "Participant has stopped the camera"

View File

@@ -12,10 +12,8 @@ import {
setPassword,
setSubject
} from '../../react/features/base/conference';
import { overwriteConfig, getWhitelistedJSON } from '../../react/features/base/config';
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
import JitsiMeetJS, { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
import { MEDIA_TYPE } from '../../react/features/base/media';
import { pinParticipant, getParticipantById, kickParticipant } from '../../react/features/base/participants';
import { setPrivateMessageRecipient } from '../../react/features/chat/actions';
import { openChat } from '../../react/features/chat/actions.web';
@@ -81,9 +79,7 @@ function initCommands() {
sendAnalytics(createApiEvent('display.name.changed'));
APP.conference.changeLocalDisplayName(displayName);
},
'mute-everyone': mediaType => {
const muteMediaType = mediaType ? mediaType : MEDIA_TYPE.AUDIO;
'mute-everyone': () => {
sendAnalytics(createApiEvent('muted-everyone'));
const participants = APP.store.getState()['features/base/participants'];
const localIds = participants
@@ -91,7 +87,7 @@ function initCommands() {
.filter(participant => participant.role === 'moderator')
.map(participant => participant.id);
APP.store.dispatch(muteAllParticipants(localIds, muteMediaType));
APP.store.dispatch(muteAllParticipants(localIds));
},
'toggle-lobby': isLobbyEnabled => {
APP.store.dispatch(toggleLobbyMode(isLobbyEnabled));
@@ -357,11 +353,6 @@ function initCommands() {
},
'kick-participant': participantId => {
APP.store.dispatch(kickParticipant(participantId));
},
'overwrite-config': config => {
const whitelistedConfig = getWhitelistedJSON('config', config);
APP.store.dispatch(overwriteConfig(whitelistedConfig));
}
};
transport.on('event', ({ data, name }) => {

View File

@@ -37,7 +37,6 @@ const commands = {
intiatePrivateChat: 'initiate-private-chat',
kickParticipant: 'kick-participant',
muteEveryone: 'mute-everyone',
overwriteConfig: 'overwrite-config',
password: 'password',
pinParticipant: 'pin-participant',
resizeLargeVideo: 'resize-large-video',
@@ -197,7 +196,7 @@ function parseArguments(args) {
* @param {any} value - The value to be parsed.
* @returns {string|undefined} The parsed value that can be used for setting
* sizes through the style property. If invalid value is passed the method
* returns undefined.
* retuns undefined.
*/
function parseSizeParam(value) {
let parsedValue;
@@ -321,7 +320,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
const frameName = `jitsiConferenceFrame${id}`;
this._frame = document.createElement('iframe');
this._frame.allow = 'camera; microphone; display-capture; autoplay; clipboard-write';
this._frame.allow = 'camera; microphone; display-capture; autoplay;';
this._frame.src = this._url;
this._frame.name = frameName;
this._frame.id = frameName;

View File

@@ -491,25 +491,6 @@ UI.onSharedVideoStop = function(id, attributes) {
}
};
/**
* Show shared video.
* @param {string} url video url
*/
UI.startSharedVideoEmitter = function(url) {
if (sharedVideoManager) {
sharedVideoManager.startSharedVideoEmitter(url);
}
};
/**
* Stop shared video.
*/
UI.stopSharedVideoEmitter = function() {
if (sharedVideoManager) {
sharedVideoManager.stopSharedVideoEmitter();
}
};
// TODO: Export every function separately. For now there is no point of doing
// this because we are importing everything.
export default UI;

View File

@@ -63,7 +63,7 @@ function doExternalAuth(room, lockPassword) {
/**
* Redirect the user to the token authentication service for the login to be
* performed. Once complete it is expected that the service will bring the user
* performed. Once complete it is expected that the service wil bring the user
* back with "?jwt={the JWT token}" query parameter added.
* @param {string} [roomName] the name of the conference room.
*/
@@ -76,7 +76,7 @@ function redirectToTokenAuthService(roomName) {
/**
* Initializes 'message' listener that will wait for a JWT token to be received
* from the token authentication service opened in a popup window.
* @param room the name of the conference room.
* @param room the name fo the conference room.
*/
function initJWTTokenListener(room) {
/**
@@ -108,7 +108,7 @@ function initJWTTokenListener(room) {
roomName, APP.conference._getConferenceOptions());
// Authenticate from the new connection to get
// the session-ID from the focus, which will then be used
// the session-ID from the focus, which wil then be used
// to upgrade current connection's user role
newRoom.room.moderator.authenticate()
@@ -116,7 +116,7 @@ function initJWTTokenListener(room) {
connection.disconnect();
// At this point we'll have session-ID stored in
// the settings. It will be used in the call below
// the settings. It wil be used in the call below
// to upgrade user's role
room.room.moderator.authenticate()
.then(() => {

View File

@@ -12,10 +12,11 @@ import {
participantLeft,
pinParticipant
} from '../../../react/features/base/participants';
import { VIDEO_PLAYER_PARTICIPANT_NAME } from '../../../react/features/shared-video/constants';
import { dockToolbox, showToolbox } from '../../../react/features/toolbox/actions.web';
import { getToolboxHeight } from '../../../react/features/toolbox/functions.web';
import { YOUTUBE_PARTICIPANT_NAME } from '../../../react/features/youtube-player/constants';
import UIEvents from '../../../service/UI/UIEvents';
import UIUtil from '../util/UIUtil';
import Filmstrip from '../videolayout/Filmstrip';
import LargeContainer from '../videolayout/LargeContainer';
import VideoLayout from '../videolayout/VideoLayout';
@@ -28,8 +29,14 @@ export const SHARED_VIDEO_CONTAINER_TYPE = 'sharedvideo';
* Example shared video link.
* @type {string}
*/
const defaultSharedVideoLink = 'https://youtu.be/TB7LlM4erx8';
const updateInterval = 5000; // milliseconds
/**
* The dialog for user input (video link).
* @type {null}
*/
let dialog = null;
/**
* Manager of shared video.
@@ -69,37 +76,52 @@ export default class SharedVideoManager {
}
/**
* Start shared video event emitter if a video is not shown.
*
* @param url of the video
* Starts shared video by asking user for url, or if its already working
* asks whether the user wants to stop sharing the video.
*/
startSharedVideoEmitter(url) {
toggleSharedVideo() {
if (dialog) {
return;
}
if (!this.isSharedVideoShown) {
if (url) {
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
sendAnalytics(createEvent('started'));
}
requestVideoLink().then(
url => {
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
sendAnalytics(createEvent('started'));
},
err => {
logger.log('SHARED VIDEO CANCELED', err);
sendAnalytics(createEvent('canceled'));
}
);
logger.log('SHARED VIDEO CANCELED');
sendAnalytics(createEvent('canceled'));
return;
}
}
/**
* Stop shared video event emitter done by the one who shared the video.
*/
stopSharedVideoEmitter() {
if (APP.conference.isLocalId(this.from)) {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
sendAnalytics(createEvent('stopped'));
showStopVideoPropmpt().then(
() => {
// make sure we stop updates for playing before we send stop
// if we stop it after receiving self presence, we can end
// up sending stop playing, and on the other end it will not
// stop
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
sendAnalytics(createEvent('stopped'));
},
() => {}); // eslint-disable-line no-empty-function
} else {
APP.UI.messageHandler.showWarning({
descriptionKey: 'dialog.alreadySharedVideoMsg',
titleKey: 'dialog.alreadySharedVideoTitle'
});
sendAnalytics(createEvent('already.shared'));
}
}
@@ -281,7 +303,7 @@ export default class SharedVideoManager {
conference: APP.conference._room,
id: self.url,
isFakeParticipant: true,
name: VIDEO_PLAYER_PARTICIPANT_NAME
name: YOUTUBE_PARTICIPANT_NAME
}));
APP.store.dispatch(pinParticipant(self.url));
@@ -498,7 +520,7 @@ export default class SharedVideoManager {
* Receives events for local audio mute/unmute by local user.
* @param muted boolena whether it is muted or not.
* @param {boolean} indicates if this mute was a result of user interaction,
* i.e. pressing the mute button or it was programmatically triggered
* i.e. pressing the mute button or it was programatically triggerred
*/
onLocalAudioMuted(muted, userInteraction) {
if (!this.player) {
@@ -653,3 +675,134 @@ class SharedVideoContainer extends LargeContainer {
return false;
}
}
/**
* Checks if given string is youtube url.
* @param {string} url string to check.
* @returns {boolean}
*/
function getYoutubeLink(url) {
const p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;// eslint-disable-line max-len
return url.match(p) ? RegExp.$1 : false;
}
/**
* Ask user if he want to close shared video.
*/
function showStopVideoPropmpt() {
return new Promise((resolve, reject) => {
const submitFunction = function(e, v) {
if (v) {
resolve();
} else {
reject();
}
};
const closeFunction = function() {
dialog = null;
};
dialog = APP.UI.messageHandler.openTwoButtonDialog({
titleKey: 'dialog.removeSharedVideoTitle',
msgKey: 'dialog.removeSharedVideoMsg',
leftButtonKey: 'dialog.Remove',
submitFunction,
closeFunction
});
});
}
/**
* Ask user for shared video url to share with others.
* Dialog validates client input to allow only youtube urls.
*/
function requestVideoLink() {
const i18n = APP.translation;
const cancelButton = i18n.generateTranslationHTML('dialog.Cancel');
const shareButton = i18n.generateTranslationHTML('dialog.Share');
const backButton = i18n.generateTranslationHTML('dialog.Back');
const linkError
= i18n.generateTranslationHTML('dialog.shareVideoLinkError');
return new Promise((resolve, reject) => {
dialog = APP.UI.messageHandler.openDialogWithStates({
state0: {
titleKey: 'dialog.shareVideoTitle',
html: `
<input name='sharedVideoUrl' type='text'
class='input-control'
data-i18n='[placeholder]defaultLink'
autofocus>`,
persistent: false,
buttons: [
{ title: cancelButton,
value: false },
{ title: shareButton,
value: true }
],
focus: ':input:first',
defaultButton: 1,
submit(e, v, m, f) { // eslint-disable-line max-params
e.preventDefault();
if (!v) {
reject('cancelled');
dialog.close();
return;
}
const sharedVideoUrl = f.sharedVideoUrl;
if (!sharedVideoUrl) {
return;
}
const urlValue
= encodeURI(UIUtil.escapeHtml(sharedVideoUrl));
const yVideoId = getYoutubeLink(urlValue);
if (!yVideoId) {
dialog.goToState('state1');
return false;
}
resolve(yVideoId);
dialog.close();
}
},
state1: {
titleKey: 'dialog.shareVideoTitle',
html: linkError,
persistent: false,
buttons: [
{ title: cancelButton,
value: false },
{ title: backButton,
value: true }
],
focus: ':input:first',
defaultButton: 1,
submit(e, v) {
e.preventDefault();
if (v === 0) {
reject();
dialog.close();
} else {
dialog.goToState('state0');
}
}
}
}, {
close() {
dialog = null;
}
}, {
url: defaultSharedVideoLink
});
});
}

View File

@@ -37,7 +37,7 @@ const UIUtil = {
* @param {string} url - The redirect URL.
* NOTE: Currently used to redirect to 3rd party location for
* authentication. In most cases redirectWithStoredParams action must be
* used instead of this method in order to preserve current URL params.
* used instead of this method in order to preserve curent URL params.
*/
redirect(url) {
window.location.href = url;

View File

@@ -221,8 +221,6 @@ export default class RemoteVideo extends SmallVideo {
streamElement.autoplay = !config.testing?.noAutoPlayVideo;
streamElement.id = `remoteVideo_${stream.getId()}`;
streamElement.mute = true;
streamElement.playsInline = true;
// Put new stream element always in front
streamElement = UIUtils.prependChild(this.container, streamElement);

View File

@@ -350,7 +350,7 @@ export default class SmallVideo {
}
/**
* Initializes any browser specific properties. Currently sets the overflow
* Initalizes any browser specific properties. Currently sets the overflow
* property for Qt browsers on Windows to hidden, thus fixing the following
* problem:
* Some browsers don't have full support of the object-fit property for the
@@ -428,9 +428,9 @@ export default class SmallVideo {
_shouldTriggerPin(event) {
// TODO Checking the classes is a workround to allow events to bubble into
// the DisplayName component if it was clicked. React's synthetic events
// will fire after jQuery handlers execute, so stop propagation at this
// will fire after jQuery handlers execute, so stop propogation at this
// point will prevent DisplayName from getting click events. This workaround
// should be removable once LocalVideo is a React Component because then
// should be removeable once LocalVideo is a React Component because then
// the components share the same eventing system.
const $source = $(event.target || event.srcElement);

View File

@@ -46,7 +46,7 @@ function computeDesktopVideoSize( // eslint-disable-line max-params
videoSpaceWidth,
videoSpaceHeight) {
if (videoWidth === 0 || videoHeight === 0 || videoSpaceWidth === 0 || videoSpaceHeight === 0) {
// Avoid NaN values caused by division by 0.
// Avoid NaN values caused by devision by 0.
return [ 0, 0 ];
}
@@ -94,7 +94,7 @@ function computeCameraVideoSize( // eslint-disable-line max-params
videoSpaceHeight,
videoLayoutFit) {
if (videoWidth === 0 || videoHeight === 0 || videoSpaceWidth === 0 || videoSpaceHeight === 0) {
// Avoid NaN values caused by division by 0.
// Avoid NaN values caused by devision by 0.
return [ 0, 0 ];
}
@@ -411,7 +411,7 @@ export class VideoContainer extends LargeContainer {
const [ width, height ] = this._getVideoSize(containerWidth, containerHeight);
if (width === 0 || height === 0) {
// We don't need to set 0 for width or height since the visibility is controlled by the visibility css prop
// We don't need to set 0 for width or height since the visibility is controled by the visibility css prop
// on the largeVideoElementsContainer. Also if the width/height of the video element is 0 the attached
// stream won't be played. Normally if we attach a new stream we won't resize the video element until the
// stream has been played. But setting width/height to 0 will prevent the video from playing.
@@ -610,7 +610,7 @@ export class VideoContainer extends LargeContainer {
// explicitly disabled.
if (interfaceConfig.DISABLE_VIDEO_BACKGROUND
|| browser.isFirefox()
|| browser.isWebKitBased()) {
|| browser.isSafari()) {
return;
}

View File

@@ -596,7 +596,7 @@ const VideoLayout = {
localVideoThumbnail && localVideoThumbnail.updateDOMLocation();
VideoLayout.resizeVideoArea();
// Rerender the thumbnails since they are dependent on the layout because of the tooltip positioning.
// Rerender the thumbnails since they are dependant on the layout because of the tooltip positioning.
localVideoThumbnail && localVideoThumbnail.rerender();
Object.values(remoteVideos).forEach(remoteVideoThumbnail => remoteVideoThumbnail.rerender());
},

View File

@@ -46,14 +46,7 @@ export class TaskQueue {
this._currentTask = this._queue.shift() || null;
if (this._currentTask) {
logger.debug('Executing a task.');
try {
this._currentTask(this._onTaskComplete);
} catch (error) {
logger.error(`Task execution failed: ${error}`);
this._onTaskComplete();
}
this._currentTask(this._onTaskComplete);
}
}
@@ -65,7 +58,6 @@ export class TaskQueue {
*/
_onTaskComplete() {
this._currentTask = null;
logger.debug('Task completed.');
this._executeNext();
}
}

160
package-lock.json generated
View File

@@ -2586,9 +2586,9 @@
}
},
"@jitsi/js-utils": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.6.tgz",
"integrity": "sha512-XdLvgzhEhMsrzHBDgBydvWneGXSZ8ycg+dEK9bs2CLQhH5A9cHOOToDl9p/skts7WHQp4C8nkhsNzWC/cRvszQ==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@jitsi/js-utils/-/js-utils-1.0.5.tgz",
"integrity": "sha512-1APQyuqQaYDR+W7cdgzsaBo6x8dpF8sfelcBf3ngNU3Jd+DzuuwUvCMTbr2+cCuy6w59ZAuQ7e2ixCnnOXOW4Q==",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3"
@@ -3781,6 +3781,68 @@
}
}
},
"@tensorflow-models/body-pix": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@tensorflow-models/body-pix/-/body-pix-2.0.4.tgz",
"integrity": "sha512-wRoEZBv2BNORDZjNNRLu1W4Td4/LRbaqSJFVWryHeBcHr5m5xSSETwnDfFRtLLofFtVNHfi7VUZ7TFjkaktNug=="
},
"@tensorflow/tfjs": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.5.1.tgz",
"integrity": "sha512-WiE+JQ3ibr5LibGiBz6HWUqLJW8HiX6ywUSCA7ehZ67vFsw4mPuVjv0WEEUfD/l47PkXYVAmWd+RYOJiuZC7Eg==",
"requires": {
"@tensorflow/tfjs-converter": "1.5.1",
"@tensorflow/tfjs-core": "1.5.1",
"@tensorflow/tfjs-data": "1.5.1",
"@tensorflow/tfjs-layers": "1.5.1"
}
},
"@tensorflow/tfjs-converter": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.5.1.tgz",
"integrity": "sha512-M9tl2/ep8ntcZpmncHwKuvThsS7TaUWqJ9vJSgJmkazwTfAvlAJmZ8/p1miJ+m5sH1EJO4oTjiEmch6g8IA5IQ=="
},
"@tensorflow/tfjs-core": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.5.1.tgz",
"integrity": "sha512-N4fsi8mLsRwRs8UJN2cARB1rYFxyVXkLyZ4wOusiR976BwwZbCwQrTTSIPzPqYT3rwiexEUzm7sM6ZaDl5dpXA==",
"requires": {
"@types/offscreencanvas": "~2019.3.0",
"@types/seedrandom": "2.4.27",
"@types/webgl-ext": "0.0.30",
"@types/webgl2": "0.0.4",
"node-fetch": "~2.1.2",
"seedrandom": "2.4.3"
},
"dependencies": {
"node-fetch": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
"integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
}
}
},
"@tensorflow/tfjs-data": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.5.1.tgz",
"integrity": "sha512-eu4X0tHS1Tng+cvMO9gkMhUWX/UZQ//VpiaZfQJfa3zvUgxw6s1MHJFb0JC1T1FOnEgDVriZ8G758ysJZOybog==",
"requires": {
"@types/node-fetch": "^2.1.2",
"node-fetch": "~2.1.2"
},
"dependencies": {
"node-fetch": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
"integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
}
}
},
"@tensorflow/tfjs-layers": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.5.1.tgz",
"integrity": "sha512-DyuhifqflK+bdpBRLAj3RuWm1eTVe8yNX2+WH+W+wmhpjGg7Yagnar6/66JdS2h3WUFoiplCpZRAVMVw631E5g=="
},
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -3840,8 +3902,20 @@
"@types/node": {
"version": "12.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz",
"integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==",
"dev": true
"integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w=="
},
"@types/node-fetch": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.4.tgz",
"integrity": "sha512-Oz6id++2qAOFuOlE1j0ouk1dzl3mmI1+qINPNBhi9nt/gVOz0G+13Ao6qjhdF0Ys+eOkhu6JnFmt38bR3H0POQ==",
"requires": {
"@types/node": "*"
}
},
"@types/offscreencanvas": {
"version": "2019.3.0",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz",
"integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q=="
},
"@types/parse-json": {
"version": "4.0.0",
@@ -3853,11 +3927,26 @@
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"@types/seedrandom": {
"version": "2.4.27",
"resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.27.tgz",
"integrity": "sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE="
},
"@types/stack-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
"integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw=="
},
"@types/webgl-ext": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz",
"integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg=="
},
"@types/webgl2": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.4.tgz",
"integrity": "sha512-PACt1xdErJbMUOUweSrbVM7gSIYm1vTncW2hF6Os/EeWi6TXYAYMPp+8v6rzHmypE5gHrxaxZNXgMkJVIdZpHw=="
},
"@types/yargs": {
"version": "13.0.8",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz",
@@ -5858,11 +5947,6 @@
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
},
"clipboard-copy": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-4.0.1.tgz",
"integrity": "sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng=="
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -6985,30 +7069,24 @@
"integrity": "sha512-T7mYop3aDpRHIQaUYcmzmh6j9MAe560n6ukqjJMbVC6bVTau7dSpvB18bcsBPPtOSe10cKxhJFtlbEzLa0LL1g=="
},
"elliptic": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"dev": true,
"requires": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.1",
"inherits": "^2.0.4",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1"
"hmac-drbg": "^1.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
},
"dependencies": {
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"dev": true
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"version": "4.11.9",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
"dev": true
}
}
@@ -10265,8 +10343,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#676c7a910505833810314a665ad1e825a158850c",
"from": "github:jitsi/lib-jitsi-meet#676c7a910505833810314a665ad1e825a158850c",
"version": "github:jitsi/lib-jitsi-meet#c534f748849a308d08b06e306f5a66709ccae056",
"from": "github:jitsi/lib-jitsi-meet#c534f748849a308d08b06e306f5a66709ccae056",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "1.0.3",
@@ -10362,9 +10440,9 @@
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash.clonedeep": {
"version": "4.5.0",
@@ -11492,9 +11570,9 @@
}
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
"version": "2.19.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.19.4.tgz",
"integrity": "sha512-1xFTAknSLfc47DIxHDUbnJWC+UwgWxATmymaxIPQpmMh7LBm7ZbwVEsuushqwL2GYZU0jie4xO+TK44hJPjNSQ=="
},
"moment-duration-format": {
"version": "2.2.2",
@@ -14077,8 +14155,9 @@
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
},
"react-native-webrtc": {
"version": "github:react-native-webrtc/react-native-webrtc#1343580c0322f265ff0fb22722ac5501c5fd77ad",
"from": "github:react-native-webrtc/react-native-webrtc#1343580c0322f265ff0fb22722ac5501c5fd77ad",
"version": "1.87.3",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.87.3.tgz",
"integrity": "sha512-fWnaEHFCFD7YnPR95aaUqLQ5b4dY4av0qHjmwHXeLHGvGrVeWF1je9PNhet7PDHUIJa4GIYKB/8+co51SXm5dA==",
"requires": {
"base64-js": "^1.1.2",
"cross-os": "^1.3.0",
@@ -14818,6 +14897,11 @@
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.3.0.tgz",
"integrity": "sha1-V6lXWUIEHYV3qGnXx01MOgvYiPY="
},
"seedrandom": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
"integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw="
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",

View File

@@ -32,16 +32,17 @@
"@atlaskit/theme": "11.0.2",
"@atlaskit/toggle": "12.0.3",
"@atlaskit/tooltip": "17.1.2",
"@jitsi/js-utils": "1.0.6",
"@jitsi/js-utils": "1.0.5",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-async-storage/async-storage": "1.13.2",
"@react-native-community/google-signin": "3.0.1",
"@react-native-community/netinfo": "4.1.5",
"@svgr/webpack": "4.3.2",
"@tensorflow-models/body-pix": "2.0.4",
"@tensorflow/tfjs": "1.5.1",
"amplitude-js": "7.3.3",
"base64-js": "1.3.1",
"bc-css-flags": "3.0.0",
"clipboard-copy": "4.0.1",
"dropbox": "4.0.9",
"focus-visible": "5.1.0",
"i18n-iso-countries": "3.7.8",
@@ -55,10 +56,10 @@
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#676c7a910505833810314a665ad1e825a158850c",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#c534f748849a308d08b06e306f5a66709ccae056",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.21",
"moment": "2.29.1",
"lodash": "4.17.19",
"moment": "2.19.4",
"moment-duration-format": "2.2.2",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"pixelmatch": "5.1.0",
@@ -84,7 +85,7 @@
"react-native-svg-transformer": "0.14.3",
"react-native-url-polyfill": "1.2.0",
"react-native-watch-connectivity": "0.4.3",
"react-native-webrtc": "github:react-native-webrtc/react-native-webrtc#1343580c0322f265ff0fb22722ac5501c5fd77ad",
"react-native-webrtc": "1.87.3",
"react-native-webview": "11.0.2",
"react-native-youtube-iframe": "1.2.3",
"react-redux": "7.1.0",
@@ -148,10 +149,7 @@
"scripts": {
"lint": "eslint . && flow",
"postinstall": "jetify",
"validate": "npm ls",
"start": "make dev",
"ios": "react-native run-ios",
"android": "react-native run-android"
"validate": "npm ls"
},
"browser": {
"jQuery-Impromptu": "jQuery-Impromptu/src/jquery-impromptu.js"

View File

@@ -48,11 +48,11 @@ export default class Toolbar extends Component<Props> {
return (
<div
className = { `toolbox-content-items always-on-top-toolbox ${className}` }
className = { `always-on-top-toolbox ${className}` }
onMouseOut = { onMouseOut }
onMouseOver = { onMouseOver }>
<AudioMuteButton />
<HangupButton customClass = 'hangup-button' />
<HangupButton />
<VideoMuteButton />
</div>
);

View File

@@ -504,17 +504,15 @@ export function createRejoinedEvent({ url, lastConferenceDuration, timeSinceLeft
*
* @param {string} participantId - The ID of the participant that was remotely
* muted.
* @param {string} mediaType - The media type of the channel to mute.
* @returns {Object} The event in a format suitable for sending via
* sendAnalytics.
*/
export function createRemoteMuteConfirmedEvent(participantId, mediaType) {
export function createRemoteMuteConfirmedEvent(participantId) {
return {
action: 'clicked',
actionSubject: 'remote.mute.dialog.confirm.button',
attributes: {
'participant_id': participantId,
'media_type': mediaType
'participant_id': participantId
},
source: 'remote.mute.dialog',
type: TYPE_UI

View File

@@ -13,6 +13,6 @@ import '../mobile/proximity/middleware';
import '../mobile/wake-lock/middleware';
import '../mobile/watchos/middleware';
import '../share-room/middleware';
import '../shared-video/middleware';
import '../youtube-player/middleware';
import './middlewares.any';

View File

@@ -25,6 +25,7 @@ import '../base/testing/reducer';
import '../base/tracks/reducer';
import '../base/user-interaction/reducer';
import '../billing-counter/reducer';
import '../blur/reducer';
import '../calendar-sync/reducer';
import '../chat/reducer';
import '../deep-linking/reducer';

View File

@@ -8,6 +8,6 @@ import '../mobile/external-api/reducer';
import '../mobile/full-screen/reducer';
import '../mobile/incoming-call/reducer';
import '../mobile/watchos/reducer';
import '../shared-video/reducer';
import '../youtube-player/reducer';
import './reducers.any';

View File

@@ -12,5 +12,5 @@ import '../remote-control/reducer';
import '../screenshot-capture/reducer';
import '../shared-video/reducer';
import '../talk-while-muted/reducer';
import '../virtual-background/reducer';
import './reducers.any';

View File

@@ -2,9 +2,9 @@
import React, { useState } from 'react';
import { translate } from '../../base/i18n';
import { Icon, IconCheck, IconCopy } from '../../base/icons';
import { translate } from '../i18n';
import { copyText } from '../util';
import { copyText } from '../../base/util';
type Props = {
@@ -49,12 +49,9 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC
*
* @returns {void}
*/
async function onClick() {
function onClick() {
setIsHovered(false);
const isCopied = await copyText(textToCopy);
if (isCopied) {
if (copyText(textToCopy)) {
setIsClicked(true);
setTimeout(() => {

View File

@@ -55,6 +55,6 @@ export default {
button: 'rgb(255, 255, 255)',
buttonToggled: 'rgb(38, 58, 76)',
buttonToggledBorder: getRGBAFormat('#a4b8d1', 0.6),
hangup: 'rgb(227,79,86)'
hangup: 'rgb(225, 45, 45)'
}
};

View File

@@ -10,7 +10,7 @@ import { getName } from '../../app/functions';
import { endpointMessageReceived } from '../../subtitles';
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
import { MEDIA_TYPE, setAudioMuted, setVideoMuted } from '../media';
import { setAudioMuted, setVideoMuted } from '../media';
import {
dominantSpeakerChanged,
getLocalParticipant,
@@ -22,7 +22,7 @@ import {
participantRoleChanged,
participantUpdated
} from '../participants';
import { getLocalTracks, replaceLocalTrack, trackAdded, trackRemoved } from '../tracks';
import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
import {
getBackendSafePath,
getBackendSafeRoomName,
@@ -72,11 +72,10 @@ declare var APP: Object;
*
* @param {JitsiConference} conference - The JitsiConference instance.
* @param {Dispatch} dispatch - The Redux dispatch function.
* @param {Object} state - The Redux state.
* @private
* @returns {void}
*/
function _addConferenceListeners(conference, dispatch, state) {
function _addConferenceListeners(conference, dispatch) {
// A simple logger for conference errors received through
// the listener. These errors are not handled now, but logged.
conference.on(JitsiConferenceEvents.CONFERENCE_ERROR,
@@ -119,12 +118,13 @@ function _addConferenceListeners(conference, dispatch, state) {
conference.on(
JitsiConferenceEvents.STARTED_MUTED,
() => {
const audioMuted = Boolean(conference.isStartAudioMuted());
const videoMuted = Boolean(conference.isStartVideoMuted());
const localTracks = getLocalTracks(state['features/base/tracks']);
const audioMuted = Boolean(conference.startAudioMuted);
const videoMuted = Boolean(conference.startVideoMuted);
sendAnalytics(createStartMutedConfigurationEvent('remote', audioMuted, videoMuted));
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${videoMuted ? 'video' : ''}`);
sendAnalytics(createStartMutedConfigurationEvent(
'remote', audioMuted, videoMuted));
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
videoMuted ? 'video' : ''}`);
// XXX Jicofo tells lib-jitsi-meet to start with audio and/or video
// muted i.e. Jicofo expresses an intent. Lib-jitsi-meet has turned
@@ -136,14 +136,6 @@ function _addConferenceListeners(conference, dispatch, state) {
// acting on Jicofo's intent without the app's knowledge.
dispatch(setAudioMuted(audioMuted));
dispatch(setVideoMuted(videoMuted));
// Remove the tracks from peerconnection as well.
for (const track of localTracks) {
if ((audioMuted && track.jitsiTrack.getType() === MEDIA_TYPE.AUDIO)
|| (videoMuted && track.jitsiTrack.getType() === MEDIA_TYPE.VIDEO)) {
replaceLocalTrack(track.jitsiTrack, null, conference);
}
}
});
// Dispatches into features/base/tracks follow:
@@ -157,9 +149,9 @@ function _addConferenceListeners(conference, dispatch, state) {
conference.on(
JitsiConferenceEvents.TRACK_MUTE_CHANGED,
(track, participantThatMutedUs) => {
(_, participantThatMutedUs) => {
if (participantThatMutedUs) {
dispatch(participantMutedUs(participantThatMutedUs, track));
dispatch(participantMutedUs(participantThatMutedUs));
}
});
@@ -456,7 +448,7 @@ export function createConference() {
dispatch(_conferenceWillJoin(conference));
_addConferenceListeners(conference, dispatch, state);
_addConferenceListeners(conference, dispatch);
sendLocalParticipant(state, conference);

View File

@@ -48,14 +48,3 @@ export const SET_CONFIG = 'SET_CONFIG';
* }
*/
export const UPDATE_CONFIG = 'UPDATE_CONFIG';
/**
* The redux action which overwrites configurations represented by the feature
* base/config. The passed on config values overwrite the current values for given props.
*
* {
* type: OVERWRITE_CONFIG,
* config: Object
* }
*/
export const OVERWRITE_CONFIG = 'OVERWRITE_CONFIG';

View File

@@ -6,13 +6,7 @@ import type { Dispatch } from 'redux';
import { addKnownDomains } from '../known-domains';
import { parseURIString } from '../util';
import {
CONFIG_WILL_LOAD,
LOAD_CONFIG_ERROR,
SET_CONFIG,
UPDATE_CONFIG,
OVERWRITE_CONFIG
} from './actionTypes';
import { CONFIG_WILL_LOAD, LOAD_CONFIG_ERROR, SET_CONFIG, UPDATE_CONFIG } from './actionTypes';
import { _CONFIG_STORE_PREFIX } from './constants';
import { setConfigFromURLParams } from './functions';
@@ -73,22 +67,6 @@ export function loadConfigError(error: Error, locationURL: URL) {
};
}
/**
* Overwrites some config values.
*
* @param {Object} config - The new options (to overwrite).
* @returns {{
* type: OVERWRITE_CONFIG,
* config: Object
* }}
*/
export function overwriteConfig(config: Object) {
return {
type: OVERWRITE_CONFIG,
config
};
}
/**
* Sets the configuration represented by the feature base/config. The
* configuration is defined and consumed by the library lib-jitsi-meet but some

View File

@@ -97,7 +97,6 @@ export default [
'disableTileView',
'displayJids',
'doNotStoreRoom',
'doNotFlipLocalVideo',
'dropbox',
'e2eping',
'enableDisplayNameInStats',
@@ -158,8 +157,6 @@ export default [
'stereo',
'subject',
'testing',
'toolbarButtons',
'useHostPageLocalStorage',
'useTurnUdp',
'videoQuality.persist',
'webrtcIceTcpDisable',

View File

@@ -6,18 +6,3 @@
* @type string
*/
export const _CONFIG_STORE_PREFIX = 'config.js';
/**
* The list of all possible UI buttons.
*
* @protected
* @type Array<string>
*/
export const 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', 'toggle-camera'
];

View File

@@ -82,7 +82,7 @@ export function overrideConfigJSON(
}
if (configObj) {
const configJSON
= getWhitelistedJSON(configName, json[configName]);
= _getWhitelistedJSON(configName, json[configName]);
if (!_.isEmpty(configJSON)) {
logger.info(
@@ -111,10 +111,11 @@ export function overrideConfigJSON(
* @param {string} configName - The config name, one of config,
* interfaceConfig, loggingConfig.
* @param {Object} configJSON - The object with keys and values to override.
* @private
* @returns {Object} - The result object only with the keys
* that are whitelisted.
*/
export function getWhitelistedJSON(configName: string, configJSON: Object): Object {
function _getWhitelistedJSON(configName, configJSON) {
if (configName === 'interfaceConfig') {
return _.pick(configJSON, INTERFACE_CONFIG_WHITELIST);
} else if (configName === 'config') {

View File

@@ -1,7 +1,5 @@
// @flow
import { TOOLBAR_BUTTONS } from './constants';
export * from './functions.any';
/**
@@ -32,15 +30,3 @@ export function getDialOutStatusUrl(state: Object): string {
export function getDialOutUrl(state: Object): string {
return state['features/base/config'].guestDialOutUrl;
}
/**
* Returns the list of enabled toolbar buttons.
*
* @param {Object} state - The redux state.
* @returns {Array<string>} - The list of enabled toolbar buttons.
*/
export function getToolbarButtons(state: Object): Array<string> {
const { toolbarButtons } = state['features/base/config'];
return Array.isArray(toolbarButtons) ? toolbarButtons : TOOLBAR_BUTTONS;
}

View File

@@ -43,7 +43,6 @@ export default [
'RECENT_LIST_ENABLED',
'REMOTE_THUMBNAIL_RATIO',
'SETTINGS_SECTIONS',
'SHARING_FEATURES',
'SHOW_CHROME_EXTENSION_BANNER',
'SHOW_DEEP_LINKING_IMAGE',
'SHOW_POWERED_BY',

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