Compare commits

...

13 Commits

Author SHA1 Message Date
damencho
62f06bc418 squash: Simplify rules. 2024-04-23 10:59:43 -05:00
damencho
8a2379077f feat: Log when affiliation is missing in admin set command.
An error for indexing nil we observe.
2024-04-23 08:13:15 -05:00
Calinteodor
219e6ce1ca feat(android): list post notifications permission (#14657)
* feat(android): list post notifications permission
2024-04-23 13:16:23 +03:00
Avram Tudor
6b2d586aee fix: allow multiple jids to be specified for sending system messages (#14669) 2024-04-23 12:05:58 +03:00
damencho
28a9850111 fix: Updates jvb user account check.
In certain cases (lib-unbound not found message from lua) we can detect that jvb account is not existing, and we will re-create causing jvb to not able to connect, as password is changed only in prosody.
2024-04-22 17:43:03 -05:00
Edgars Voroboks
d9ef9dc6a0 fix(lang): Update missing Latvian lang translation (#14664) 2024-04-22 09:41:29 +02:00
Hristo Terezov
88b6cdf39b ref(settings): remove changeLocalDisplayName action 2024-04-21 19:42:41 -05:00
Hristo Terezov
e3ab6c9f33 ref(participants): remove unused action. 2024-04-21 19:42:41 -05:00
Hristo Terezov
9bb27b83d9 ref(modules/UI): remove events system.
Many of the events are not used at all or used only on one place. For the rest of them the listeners were added 2 times on promoted visitors and not cleaned at all.
2024-04-21 19:42:41 -05:00
Christoph Settgast
d8b0710a19 ci: move to node 20 based GH actions as current ones are deprecated (#14663) 2024-04-21 22:50:26 +02:00
Roland Meyer
b02b7ac769 lang: update German translation (#14662) 2024-04-21 11:46:08 +02:00
Jaya Allamsetty
b90e187a73 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1812.0.0+2eddb859...v1813.0.0+09993fc8
2024-04-19 11:19:01 -04:00
kerem
582bbf890b fix descriptions
fix descriptions
2024-04-19 15:27:31 +03:00
28 changed files with 220 additions and 374 deletions

View File

@@ -7,7 +7,7 @@ jobs:
name: Luacheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install luarocks
run: sudo apt-get --install-recommends -y install luarocks

View File

@@ -7,8 +7,8 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'
@@ -34,8 +34,8 @@ jobs:
name: Build Frontend (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'
@@ -45,8 +45,8 @@ jobs:
name: Build Frontend (macOS)
runs-on: macOS-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'
@@ -56,8 +56,8 @@ jobs:
name: Build mobile bundle (Android)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'
@@ -67,8 +67,8 @@ jobs:
name: Build mobile bundle (iOS)
runs-on: macOS-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'
@@ -89,8 +89,8 @@ jobs:
name: Test Debian packages build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
cache: 'npm'

View File

@@ -14,6 +14,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature
android:glEsVersion="0x00020000"

View File

@@ -16,6 +16,8 @@
package org.jitsi.meet.sdk;
import static android.Manifest.permission.POST_NOTIFICATIONS;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
@@ -24,6 +26,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Bundle;
@@ -31,6 +34,8 @@ import android.os.IBinder;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.facebook.react.modules.core.PermissionListener;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
@@ -52,12 +57,13 @@ public class JitsiMeetOngoingConferenceService extends Service
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
private static final int POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
private boolean isAudioMuted;
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
public static void launch(Context context, HashMap<String, Object> extraData) {
private static void doLaunch(Context context, HashMap<String, Object> extraData) {
OngoingNotification.createNotificationChannel((Activity) context);
@@ -87,6 +93,31 @@ public class JitsiMeetOngoingConferenceService extends Service
}
}
public static void launch(Context context, HashMap<String, Object> extraData) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PermissionListener listener = new PermissionListener() {
@Override
public boolean onRequestPermissionsResult(int i, String[] strings, int[] results) {
if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
doLaunch(context, extraData);
}
return true;
}
};
JitsiMeetActivityDelegate.requestPermissions(
(Activity) context,
new String[]{POST_NOTIFICATIONS},
POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE,
listener
);
} else {
doLaunch(context, extraData);
}
}
public static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent);

View File

@@ -2,7 +2,6 @@
import { jitsiLocalStorage } from '@jitsi/js-utils';
import Logger from '@jitsi/logger';
import EventEmitter from 'events';
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
@@ -165,12 +164,9 @@ import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/Au
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
import { muteLocal } from './react/features/video-menu/actions.any';
import UIEvents from './service/UI/UIEvents';
const logger = Logger.getLogger(__filename);
const eventEmitter = new EventEmitter();
let room;
/*
@@ -1916,21 +1912,12 @@ export default {
JitsiE2ePingEvents.E2E_RTT_CHANGED,
(...args) => APP.store.dispatch(e2eRttChanged(...args)));
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
this.muteAudio(muted);
});
APP.UI.addListener(UIEvents.VIDEO_MUTED, (muted, showUI = false) => {
this.muteVideo(muted, showUI);
});
room.addCommandListener(this.commands.defaults.ETHERPAD,
({ value }) => {
APP.UI.initEtherpad(value);
}
);
APP.UI.addListener(UIEvents.EMAIL_CHANGED,
this.changeLocalEmail.bind(this));
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
APP.store.dispatch(participantUpdated({
conference: room,
@@ -1950,9 +1937,6 @@ export default {
}));
});
APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
this.changeLocalDisplayName.bind(this));
room.on(
JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
({ audio, video }) => {
@@ -2007,115 +1991,119 @@ export default {
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
}
);
},
// call hangup
APP.UI.addListener(UIEvents.HANGUP, () => {
this.hangup(true);
/**
* Handles audio device changes.
*
* @param {string} cameraDeviceId - The new device id.
* @returns {Promise}
*/
async onAudioDeviceChanged(micDeviceId) {
const audioWasMuted = this.isLocalAudioMuted();
// Disable noise suppression if it was enabled on the previous track.
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
const isDefaultMicSelected = micDeviceId === 'default';
const selectedDeviceId = isDefaultMicSelected
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
: micDeviceId;
logger.info(`Switching audio input device to ${selectedDeviceId}`);
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
createLocalTracksF({
devices: [ 'audio' ],
micDeviceId: selectedDeviceId
})
.then(([ stream ]) => {
// if audio was muted before changing the device, mute
// with the new device
if (audioWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(async stream => {
await this._maybeApplyAudioMixerEffect(stream);
return this.useAudioStream(stream);
})
.then(() => {
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
if (localAudio && isDefaultMicSelected) {
// workaround for the default device to be shown as selected in the
// settings even when the real device id was passed to gUM because of the
// above mentioned chrome bug.
localAudio._realDeviceId = localAudio.deviceId = 'default';
}
})
.catch(err => {
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
APP.store.dispatch(notifyMicError(err));
});
},
APP.UI.addListener(
UIEvents.VIDEO_DEVICE_CHANGED,
cameraDeviceId => {
const videoWasMuted = this.isLocalVideoMuted();
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
/**
* Handles video device changes.
*
* @param {string} cameraDeviceId - The new device id.
* @returns {void}
*/
onVideoDeviceChanged(cameraDeviceId) {
const videoWasMuted = this.isLocalVideoMuted();
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
return;
}
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
return;
}
sendAnalytics(createDeviceChangedEvent('video', 'input'));
sendAnalytics(createDeviceChangedEvent('video', 'input'));
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(stream => {
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
return this.useVideoStream(stream);
})
.catch(error => {
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
return APP.store.dispatch(notifyCameraError(error));
});
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
);
APP.UI.addListener(
UIEvents.AUDIO_DEVICE_CHANGED,
async micDeviceId => {
const audioWasMuted = this.isLocalAudioMuted();
return stream;
})
.then(stream => {
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
// Disable noise suppression if it was enabled on the previous track.
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
return this.useVideoStream(stream);
})
.catch(error => {
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
const isDefaultMicSelected = micDeviceId === 'default';
const selectedDeviceId = isDefaultMicSelected
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
: micDeviceId;
logger.info(`Switching audio input device to ${selectedDeviceId}`);
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
createLocalTracksF({
devices: [ 'audio' ],
micDeviceId: selectedDeviceId
})
.then(([ stream ]) => {
// if audio was muted before changing the device, mute
// with the new device
if (audioWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(async stream => {
await this._maybeApplyAudioMixerEffect(stream);
return this.useAudioStream(stream);
})
.then(() => {
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
if (localAudio && isDefaultMicSelected) {
// workaround for the default device to be shown as selected in the
// settings even when the real device id was passed to gUM because of the
// above mentioned chrome bug.
localAudio._realDeviceId = localAudio.deviceId = 'default';
}
})
.catch(err => {
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
APP.store.dispatch(notifyMicError(err));
});
}
);
APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, () => {
// Immediately update the UI by having remote videos and the large video update themselves.
const displayedUserId = APP.UI.getLargeVideoID();
if (displayedUserId) {
APP.UI.updateLargeVideo(displayedUserId, true);
}
return APP.store.dispatch(notifyCameraError(error));
});
},
/**
* Handles audio only changes.
*/
onToggleAudioOnly() {
// Immediately update the UI by having remote videos and the large video update themselves.
const displayedUserId = APP.UI.getLargeVideoID();
if (displayedUserId) {
APP.UI.updateLargeVideo(displayedUserId, true);
}
},
/**
* Cleanups local conference on suspend.
*/
@@ -2408,8 +2396,6 @@ export default {
this.deviceChangeListener);
}
APP.UI.removeAllListeners();
let feedbackResultPromise = Promise.resolve({});
if (requestFeedback) {
@@ -2516,37 +2502,6 @@ export default {
room.sendEndpointMessage(to, payload);
},
/**
* Adds new listener.
* @param {String} eventName the name of the event
* @param {Function} listener the listener.
*/
addListener(eventName, listener) {
eventEmitter.addListener(eventName, listener);
},
/**
* Removes listener.
* @param {String} eventName the name of the event that triggers the
* listener
* @param {Function} listener the listener.
*/
removeListener(eventName, listener) {
eventEmitter.removeListener(eventName, listener);
},
/**
* Changes the display name for the local user
* @param nickname {string} the new display name
*/
changeLocalDisplayName(nickname = '') {
const formattedNickname = getNormalizedDisplayName(nickname);
APP.store.dispatch(updateSettings({
displayName: formattedNickname
}));
},
/**
* Callback invoked by the external api create or update a direct connection
* from the local client to an external client.

View File

@@ -138,10 +138,8 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true`
if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then
prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true
fi
# creates the user if it does not exist
echo -e "$JVB_SECRET\n$JVB_SECRET" | prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN > /dev/null || true
# Check whether prosody config has the internal muc, if not add it,
# as we are migrating configs

View File

@@ -128,6 +128,7 @@
"privateNotice": "Private Nachricht an {{recipient}}",
"sendButton": "Senden",
"smileysPanel": "Emoji-Auswahl",
"systemDisplayName": "System",
"tabs": {
"chat": "Chatten",
"polls": "Umfragen"
@@ -305,6 +306,8 @@
"contactSupport": "Support kontaktieren",
"copied": "Kopiert",
"copy": "Kopieren",
"demoteParticipantDialog": "Sind Sie sicher, dass Sie diese Person zu den Gästen verschieben möchten?",
"demoteParticipantTitle": "Zu Gästen verschieben",
"dismiss": "OK",
"displayNameRequired": "Hallo! Wie ist Ihr Name?",
"done": "Fertig",
@@ -560,6 +563,7 @@
"noNumbers": "Keine Telefonnummern verfügbar.",
"noPassword": "Kein Passwort benötigt",
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
"noWhiteboard": "Whiteboard konnte nicht geladen werden.",
"numbers": "Einwahlnummern",
"password": "$t(lockRoomPasswordUppercase):",
"reachedLimit": "Sie haben die Grenzen Ihres Tarifs erreicht.",
@@ -567,7 +571,8 @@
"sipAudioOnly": "SIP-Adresse (nur Ton)",
"title": "Teilen",
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf"
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf",
"whiteboardError": "Whiteboard konnte nicht geladen werden. Bitte versuchen Sie es später erneut."
},
"inlineDialogFailure": {
"msg": "Es ist ein Fehler aufgetreten.",
@@ -802,12 +807,16 @@
"startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!",
"suboptimalBrowserWarning": "Tut uns leid, aber die Konferenz wird mit {{appName}} kein großartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"{{recommendedBrowserPageLink}}\" target=\"_blank\">vollständig unterstützen Browser</a>.",
"suboptimalExperienceTitle": "Browserwarnung",
"suggestRecordingAction": "Starten",
"suggestRecordingDescription": "Möchten Sie eine Aufzeichnung starten?",
"suggestRecordingTitle": "Konferenz aufzeichnen",
"unmute": "Stummschaltung aufheben",
"videoMutedRemotelyDescription": "Sie können sie jederzeit wieder einschalten.",
"videoMutedRemotelyTitle": "Ihre Kamera wurde von {{participantDisplayName}} ausgeschaltet!",
"videoUnmuteBlockedDescription": "Die Kamera und Bildschirmfreigabe kann aus Überlastungsschutzgründen temporär nicht eingeschaltet werden.",
"videoUnmuteBlockedTitle": "Kamera und Bildschirmfreigabe kann nicht aktiviert werden!",
"viewLobby": "Lobby ansehen",
"viewVisitors": "Gäste anzeigen",
"waitingParticipants": "{{waitingParticipants}} Personen",
"whiteboardLimitDescription": "Bitte speichern Sie Ihre Inhalte, da das Nutzungslimit bald erreicht wird und dann Ihr Whiteboard geschlossen wird.",
"whiteboardLimitTitle": "Whiteboard-Nutzung"
@@ -937,6 +946,7 @@
"or": "oder",
"premeeting": "Vorschau",
"proceedAnyway": "Trotzdem fortsetzen",
"recordingWarning": "Diese Konferenz wird möglicherweise von anderen Personen aufgezeichnet",
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
"showScreen": "Konferenzvorschau aktivieren",
"startWithPhone": "Mit Telefonaudio starten",
@@ -1003,7 +1013,6 @@
"limitNotificationDescriptionNative": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <3>{{app}}</3>.",
"limitNotificationDescriptionWeb": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
"live": "LIVE",
"localRecordingNoNotificationWarning": "Die Aufzeichnung wird anderen Anwesenden nicht mitgeteilt. Sie müssen diese selbst darauf hinweisen, dass die Konferenz aufgezeichnet wird.",
"localRecordingNoVideo": "Videos werden nicht aufgenommen",
"localRecordingStartWarning": "Bitte beenden Sie die Aufzeichnung vor dem Verlassen der Konferenz, um die Aufzeichnung zu speichern.",
@@ -1020,7 +1029,6 @@
"onBy": "{{name}} startete die Aufnahme",
"onlyRecordSelf": "Nur eigenes Kamerabild und Ton aufzeichnen",
"pending": "Aufzeichnung des Meetings wird vorbereitet…",
"rec": "AUFZ",
"recordAudioAndVideo": "Kamera und Ton aufzeichnen",
"recordTranscription": "Transkription aufzeichnen",
"saveLocalRecording": "Aufzeichnung lokal abspeichern",
@@ -1177,7 +1185,7 @@
"audioOnly": "„Nur Audio“ ein-/ausschalten",
"audioRoute": "Audiogerät auswählen",
"boo": "Buhen",
"breakoutRoom": "Breakout-Räume betreten/verlassen",
"breakoutRooms": "Breakout-Räume",
"callQuality": "Qualitätseinstellungen",
"carmode": "Automodus",
"cc": "Untertitel ein-/ausschalten",
@@ -1360,13 +1368,9 @@
},
"transcribing": {
"ccButtonTooltip": "Untertitel ein-/ausschalten",
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
"expandedLabel": "Transkribieren ist derzeit eingeschaltet",
"failedToStart": "Transkribieren konnte nicht gestartet werden",
"labelToolTip": "Das Meeting wird transkribiert",
"off": "Transkribieren gestoppt",
"on": "Transkribieren gestartet",
"pending": "Transkribieren des Meetings wird vorbereitet…",
"sourceLanguageDesc": "Aktuell ist die Sprache der Konferenz auf <b>{{sourceLanguage}}</b> eingestellt. <br/> Sie könne dies hier ",
"sourceLanguageHere": "ändern",
"start": "Anzeige der Untertitel starten",
@@ -1422,6 +1426,7 @@
},
"videothumbnail": {
"connectionInfo": "Verbindungsinformationen",
"demote": "Zu Gästen verschieben",
"domute": "Stummschalten",
"domuteOthers": "Alle anderen stummschalten",
"domuteVideo": "Kamera ausschalten",
@@ -1476,6 +1481,7 @@
"chatIndicator": "(Gast)",
"labelTooltip": "Anzahl Gäste: {{count}}",
"notification": {
"demoteDescription": "Hierhin verschoben von {{actor}}, bitte melden Sie sich um teilzunehmen",
"description": "Bitte melden Sie sich um teilzunehmen",
"title": "Sie sind Gast in der Konferenz"
}
@@ -1536,6 +1542,7 @@
"whiteboard": {
"accessibilityLabel": {
"heading": "Whiteboard"
}
},
"screenTitle": "Whiteboard"
}
}

View File

@@ -128,6 +128,7 @@
"privateNotice": "Privāta ziņa adresātam {{recipient}}",
"sendButton": "Nosūtīt",
"smileysPanel": "Emociju panelis",
"systemDisplayName": "Sistēma",
"tabs": {
"chat": "Tērzēšana",
"polls": "Aptaujas"

View File

@@ -41,6 +41,7 @@ import {
import { LOCAL_PARTICIPANT_DEFAULT_ID } from '../../react/features/base/participants/constants';
import {
getLocalParticipant,
getNormalizedDisplayName,
getParticipantById,
getScreenshareParticipantIds,
getVirtualScreenshareParticipantByOwnerId,
@@ -76,6 +77,7 @@ import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/act
import {
addStageParticipant,
resizeFilmStrip,
setFilmstripVisible,
setVolume,
togglePinStageParticipant
} from '../../react/features/filmstrip/actions.web';
@@ -199,7 +201,7 @@ function initCommands() {
},
'display-name': displayName => {
sendAnalytics(createApiEvent('display.name.changed'));
APP.conference.changeLocalDisplayName(displayName);
APP.store.dispatch(updateSettings({ displayName: getNormalizedDisplayName(displayName) }));
},
'local-subject': localSubject => {
sendAnalytics(createApiEvent('local.subject.changed'));
@@ -376,7 +378,9 @@ function initCommands() {
},
'toggle-film-strip': () => {
sendAnalytics(createApiEvent('film.strip.toggled'));
APP.UI.toggleFilmstrip();
const { visible } = APP.store.getState()['features/filmstrip'];
APP.store.dispatch(setFilmstripVisible(!visible));
},
/*

View File

@@ -4,7 +4,6 @@
const UI = {};
import Logger from '@jitsi/logger';
import EventEmitter from 'events';
import {
conferenceWillInit
@@ -13,7 +12,6 @@ import { isMobileBrowser } from '../../react/features/base/environment/utils';
import { setColorAlpha } from '../../react/features/base/util/helpers';
import { sanitizeUrl } from '../../react/features/base/util/uri';
import { setDocumentUrl } from '../../react/features/etherpad/actions';
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
import {
setNotificationsEnabled,
showNotification
@@ -25,7 +23,6 @@ import {
setToolboxEnabled,
showToolbox
} from '../../react/features/toolbox/actions.web';
import UIEvents from '../../service/UI/UIEvents';
import EtherpadManager from './etherpad/Etherpad';
import UIUtil from './util/UIUtil';
@@ -33,22 +30,8 @@ import VideoLayout from './videolayout/VideoLayout';
const logger = Logger.getLogger(__filename);
const eventEmitter = new EventEmitter();
UI.eventEmitter = eventEmitter;
let etherpadManager;
const UIListeners = new Map([
[
UIEvents.ETHERPAD_CLICKED,
() => etherpadManager && etherpadManager.toggleEtherpad()
], [
UIEvents.TOGGLE_FILMSTRIP,
() => UI.toggleFilmstrip()
]
]);
/**
* Indicates if we're currently in full screen mode.
*
@@ -96,10 +79,11 @@ UI.start = function() {
};
/**
* Setup some UI event listeners.
* Handles etherpad click.
*/
UI.registerListeners
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
UI.onEtherpadClicked = function() {
etherpadManager && etherpadManager.toggleEtherpad();
};
/**
*
@@ -143,7 +127,7 @@ UI.initEtherpad = name => {
}
logger.log('Etherpad is enabled');
etherpadManager = new EtherpadManager(eventEmitter);
etherpadManager = new EtherpadManager();
const url = new URL(name, etherpadBaseUrl);
@@ -197,15 +181,6 @@ UI.updateUserStatus = (user, status) => {
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
};
/**
* Toggles filmstrip.
*/
UI.toggleFilmstrip = function() {
const { visible } = APP.store.getState()['features/filmstrip'];
APP.store.dispatch(setFilmstripVisible(!visible));
};
/**
* Sets muted video state for participant
*/
@@ -219,33 +194,6 @@ UI.setVideoMuted = function(id) {
UI.updateLargeVideo = (id, forceUpdate) => VideoLayout.updateLargeVideo(id, forceUpdate);
/**
* Adds a listener that would be notified on the given type of event.
*
* @param type the type of the event we're listening for
* @param listener a function that would be called when notified
*/
UI.addListener = function(type, listener) {
eventEmitter.on(type, listener);
};
/**
* Removes the all listeners for all events.
*
* @returns {void}
*/
UI.removeAllListeners = function() {
eventEmitter.removeAllListeners();
};
/**
* Emits the event of given type by specifying the parameters in options.
*
* @param type the type of the event we're emitting
* @param options the parameters for the event
*/
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
// Used by torture.
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));

View File

@@ -138,8 +138,7 @@ export default class EtherpadManager {
/**
*
*/
constructor(eventEmitter) {
this.eventEmitter = eventEmitter;
constructor() {
this.etherpad = null;
}

10
package-lock.json generated
View File

@@ -61,7 +61,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1813.0.0+09993fc8/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -12866,8 +12866,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"integrity": "sha512-svKmkJzs6BNrOgTHST7aAxpGHLAVAlxuX7VQrGVL8HUpU6FDrqHBRSMQXX0937jir6OpIhSCjoFK6maZ9xfOZg==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1813.0.0+09993fc8/lib-jitsi-meet.tgz",
"integrity": "sha512-b2qDjrDHEeaxxGbQW4OWUKDQyLvWMDZtv85CJ0zqk1ZPr+Ea3OvQP329GPfKA/0Sjm//k+bKiNFH99WDvoSbrg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -29374,8 +29374,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"integrity": "sha512-svKmkJzs6BNrOgTHST7aAxpGHLAVAlxuX7VQrGVL8HUpU6FDrqHBRSMQXX0937jir6OpIhSCjoFK6maZ9xfOZg==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1813.0.0+09993fc8/lib-jitsi-meet.tgz",
"integrity": "sha512-b2qDjrDHEeaxxGbQW4OWUKDQyLvWMDZtv85CJ0zqk1ZPr+Ea3OvQP329GPfKA/0Sjm//k+bKiNFH99WDvoSbrg==",
"requires": {
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",

View File

@@ -67,7 +67,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1813.0.0+09993fc8/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",

View File

@@ -95,7 +95,7 @@ https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
## Using JWT tokens
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
- If you are planning to use tokens or another domain, you can do that by updating the following props, as shown below.
- For example:
```javascript
<JitsiMeeting
@@ -105,7 +105,7 @@ https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
```
## Using custom overflow menu buttons
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
- If you are planning to add custom overflow menu buttons, you can do that by updating the ```config``` prop, as shown below.
- For example:
```javascript
<JitsiMeeting

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { createAudioOnlyChangedEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IStore } from '../../app/types';
@@ -33,7 +31,7 @@ export function setAudioOnly(audioOnly: boolean) {
if (typeof APP !== 'undefined') {
// TODO This should be a temporary solution that lasts only until video
// tracks and all ui is moved into react/redux on the web.
APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly);
APP.conference.onToggleAudioOnly();
}
}
};

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { createStartMutedConfigurationEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IReduxState, IStore } from '../../app/types';
@@ -1083,7 +1081,7 @@ export function redirect(vnode: string, focusJid: string, username: string) {
dispatch(setAudioMuted(false, true));
// // FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined' || APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false);
typeof APP === 'undefined' || APP.conference.muteAudio(false);
}
}
@@ -1096,7 +1094,7 @@ export function redirect(vnode: string, focusJid: string, username: string) {
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true));
// // FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined' || APP.UI.emitEvent(UIEvents.VIDEO_MUTED, false);
typeof APP === 'undefined' || APP.conference.muteVideo(false, false);
}
}
}

View File

@@ -1,7 +1,5 @@
import { AnyAction } from 'redux';
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { IStore } from '../../app/types';
import { processExternalDeviceRequest } from '../../device-selection/functions';
import { showNotification, showWarningNotification } from '../../notifications/actions';
@@ -161,7 +159,7 @@ MiddlewareRegistry.register(store => next => action => {
if (isPrejoinPageVisible(store.getState())) {
store.dispatch(replaceAudioTrackById(action.deviceId));
} else {
APP.UI.emitEvent(UIEvents.AUDIO_DEVICE_CHANGED, action.deviceId);
APP.conference.onAudioDeviceChanged(action.deviceId);
}
break;
case SET_VIDEO_INPUT_DEVICE: {
@@ -174,7 +172,7 @@ MiddlewareRegistry.register(store => next => action => {
if (isPrejoinPageVisible(store.getState())) {
store.dispatch(replaceVideoTrackById(action.deviceId));
} else {
APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId);
APP.conference.onVideoDeviceChanged(action.deviceId);
}
break;
}

View File

@@ -43,18 +43,6 @@ export const KICK_PARTICIPANT = 'KICK_PARTICIPANT';
*/
export const MUTE_REMOTE_PARTICIPANT = 'MUTE_REMOTE_PARTICIPANT';
/**
* Create an action for when the local participant's display name is updated.
*
* {
* type: PARTICIPANT_DISPLAY_NAME_CHANGED,
* id: string,
* name: string
* }
*/
export const PARTICIPANT_DISPLAY_NAME_CHANGED
= 'PARTICIPANT_DISPLAY_NAME_CHANGED';
/**
* Action to signal that ID of participant has changed. This happens when
* local participant joins a new conference or quits one.

View File

@@ -2,8 +2,6 @@ import i18n from 'i18next';
import { batch } from 'react-redux';
import { AnyAction } from 'redux';
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { IStore } from '../../app/types';
import { approveParticipant } from '../../av-moderation/actions';
import { UPDATE_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes';
@@ -41,7 +39,6 @@ import {
MUTE_REMOTE_PARTICIPANT,
OVERWRITE_PARTICIPANTS_NAMES,
OVERWRITE_PARTICIPANT_NAME,
PARTICIPANT_DISPLAY_NAME_CHANGED,
PARTICIPANT_JOINED,
PARTICIPANT_LEFT,
PARTICIPANT_UPDATED,
@@ -234,20 +231,6 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
// TODO Remove this middleware when the local display name update flow is
// fully brought into redux.
case PARTICIPANT_DISPLAY_NAME_CHANGED: {
if (typeof APP !== 'undefined') {
const participant = getLocalParticipant(store.getState());
if (participant && participant.id === action.id) {
APP.UI.emitEvent(UIEvents.NICKNAME_CHANGED, action.name);
}
}
break;
}
case RAISE_HAND_UPDATED: {
const { participant } = action;
let queue = getRaiseHandsQueue(store.getState());

View File

@@ -368,8 +368,6 @@ class Conference extends AbstractConference<IProps, any> {
*/
_start() {
APP.UI.start();
APP.UI.registerListeners();
APP.UI.bindEvents();
FULL_SCREEN_EVENTS.forEach(name =>

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../service/UI/UIEvents';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
@@ -41,7 +39,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
}
case TOGGLE_DOCUMENT_EDITING: {
if (typeof APP !== 'undefined') {
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
APP.UI.onEtherpadClicked();
}
break;
}

View File

@@ -1,7 +1,5 @@
import { AnyAction } from 'redux';
// @ts-expect-error
import UIEvents from '../../../service/UI/UIEvents';
import { IStore } from '../app/types';
import {
CONFERENCE_FAILED,
@@ -36,12 +34,6 @@ MiddlewareRegistry.register(store => next => action => {
return _conferenceJoined(store, next, action);
case LOCK_STATE_CHANGED: {
// TODO Remove this logic when all components interested in the lock
// state change event are moved into react/redux.
if (typeof APP !== 'undefined') {
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK, action.locked);
}
const previousLockedState = store.getState()['features/base/conference'].locked;
const result = next(action);

View File

@@ -12,6 +12,7 @@ import { hangup } from '../base/connection/actions.web';
import { openDialog } from '../base/dialog/actions';
import i18next from '../base/i18n/i18next';
import { browser } from '../base/lib-jitsi-meet';
import { getNormalizedDisplayName } from '../base/participants/functions';
import { updateSettings } from '../base/settings/actions';
import { getLocalVideoTrack } from '../base/tracks/functions.web';
import { appendURLHashParam } from '../base/util/uri';
@@ -191,7 +192,7 @@ export function submitProfileTab(newState: any) {
const currentState = getProfileTabProps(getState());
if (newState.displayName !== currentState.displayName) {
APP.conference.changeLocalDisplayName(newState.displayName);
dispatch(updateSettings({ displayName: getNormalizedDisplayName(newState.displayName) }));
}
if (newState.email !== currentState.email) {

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../service/UI/UIEvents';
import { VIDEO_MUTE, createToolbarEvent } from '../analytics/AnalyticsEvents';
import { sendAnalytics } from '../analytics/functions';
import { IStore } from '../app/types';
@@ -103,7 +101,7 @@ export function handleToggleVideoMuted(muted: boolean, showUI: boolean, ensureTr
// FIXME: The old conference logic still relies on this event being
// emitted.
typeof APP === 'undefined'
|| APP.UI.emitEvent(UIEvents.VIDEO_MUTED, muted, showUI);
|| APP.conference.muteVideo(muted, showUI);
};
}

View File

@@ -1,5 +1,3 @@
// @ts-expect-error
import UIEvents from '../../../service/UI/UIEvents';
import {
AUDIO_MUTE,
VIDEO_MUTE,
@@ -55,8 +53,9 @@ export function muteLocal(enable: boolean, mediaType: MediaType, stopScreenShari
: setVideoMuted(enable, VIDEO_MUTISM_AUTHORITY.USER, /* ensureTrack */ true));
// FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined'
|| APP.UI.emitEvent(isAudio ? UIEvents.AUDIO_MUTED : UIEvents.VIDEO_MUTED, enable);
if (typeof APP !== 'undefined') {
isAudio ? APP.conference.muteAudio(enable) : APP.conference.muteVideo(enable, false);
}
};
}

View File

@@ -133,6 +133,12 @@ function filter_admin_set_query(event)
local room = get_room_from_jid(room_jid);
local item = stanza.tags[1].tags[1];
if not item then
module:log('error', 'Error unexpected stanza: %s', stanza);
return true;
end
local _aff = item.attr.affiliation;
-- if it is a moderated room we skip it
@@ -141,7 +147,7 @@ function filter_admin_set_query(event)
end
-- any revoking is disabled, everyone should be owners
if _aff == 'none' or _aff == 'outcast' or _aff == 'member' then
if _aff and _aff ~= 'owner' then
origin.send(st.error_reply(stanza, "auth", "forbidden"));
return true;
end

View File

@@ -1,8 +1,8 @@
-- Module which can be used as an http endpoint to send system chat messages to meeting participants. The provided token
-- Module which can be used as an http endpoint to send system private chat messages to meeting participants. The provided token
--- in the request is verified whether it has the right to do so. This module should be loaded under the virtual host.
-- Copyright (C) 2024-present 8x8, Inc.
-- curl https://{host}/send-system-message -d '{"message": "testmessage", "to": "{connection_jid}", "room": "{room_jid}"}' -H "content-type: application/json" -H "authorization: Bearer {token}"
-- curl https://{host}/send-system-chat-message -d '{"message": "testmessage", "connectionJIDs": ["{connection_jid}"], "room": "{room_jid}"}' -H "content-type: application/json" -H "authorization: Bearer {token}"
local util = module:require "util";
local token_util = module:require "token/util".new(module);
@@ -60,11 +60,11 @@ function handle_send_system_message (event)
local displayName = payload["displayName"];
local message = payload["message"];
local to = payload["to"];
local connectionJIDs = payload["connectionJIDs"];
local payload_room = payload["room"];
if not message or not to or not payload_room then
module:log("error", "One of [message, to, room] was not provided");
if not message or not connectionJIDs or not payload_room then
module:log("error", "One of [message, connectionJIDs, room] was not provided");
return { status_code = 400; }
end
@@ -99,15 +99,17 @@ function handle_send_system_message (event)
message = message,
};
local stanza = st.message({
from = room.jid,
to = to
})
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
:text(json.encode(data))
:up();
for _, to in ipairs(connectionJIDs) do
local stanza = st.message({
from = room.jid,
to = to
})
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
:text(json.encode(data))
:up();
room:route_stanza(stanza);
room:route_stanza(stanza);
end
return { status_code = 200 };
end

View File

@@ -1,57 +0,0 @@
export default {
NICKNAME_CHANGED: 'UI.nickname_changed',
/**
* Notifies that local user changed email.
*/
EMAIL_CHANGED: 'UI.email_changed',
/**
* Notifies that "start muted" settings changed.
*/
AUDIO_MUTED: 'UI.audio_muted',
VIDEO_MUTED: 'UI.video_muted',
ETHERPAD_CLICKED: 'UI.etherpad_clicked',
/**
* Updates shared video with params: url, state, time(optional)
* Where url is the video link, state is stop/start/pause and time is the
* current video playing time.
*/
TOGGLE_FULLSCREEN: 'UI.toogle_fullscreen',
FULLSCREEN_TOGGLED: 'UI.fullscreen_toggled',
/**
* Notifies that the audio only mode was toggled.
*/
TOGGLE_AUDIO_ONLY: 'UI.toggle_audioonly',
/**
* Notifies that a command to toggle the filmstrip has been issued. The
* event may optionally specify a {Boolean} (primitive) value to assign to
* the visibility of the filmstrip (i.e. the event may act as a setter).
* The very toggling of the filmstrip may or may not occurred at the time
* of the receipt of the event depending on the position of the receiving
* event listener in relation to the event listener which carries out the
* command to toggle the filmstrip.
*
* @see {TOGGLED_FILMSTRIP}
*/
TOGGLE_FILMSTRIP: 'UI.toggle_filmstrip',
HANGUP: 'UI.hangup',
VIDEO_DEVICE_CHANGED: 'UI.video_device_changed',
AUDIO_DEVICE_CHANGED: 'UI.audio_device_changed',
/**
* Notifies that the side toolbar container has been toggled. The actual
* event must contain the identifier of the container that has been toggled
* and information about toggle on or off.
*/
SIDE_TOOLBAR_CONTAINER_TOGGLED: 'UI.side_container_toggled',
/**
* Notifies that the raise hand has been changed.
*/
LOCAL_RAISE_HAND_CHANGED: 'UI.local_raise_hand_changed'
};