Compare commits

...

40 Commits
1069 ... 1111

Author SHA1 Message Date
Дамян Минков
644e12929c Merge pull request #733 from mbell8903/patch-1
Change reference to lib-jitsi-meet to use full github url
2016-07-19 10:50:06 -05:00
hristoterezov
5b884806d2 Merge pull request #736 from jitsi/lock-error-dialog
Avoids multiple dialog when showing lock attempt errors.
2016-07-15 16:02:26 -05:00
damencho
7aa47647f0 Avoids multiple dialog when showing lock attempt errors. 2016-07-15 15:40:18 -05:00
Дамян Минков
c779dbe8ad Merge pull request #735 from jitsi/fix_typo
Fixes typo
2016-07-15 13:18:57 -05:00
Дамян Минков
64ee01d831 Merge pull request #734 from jitsi/no_audio_element_fix
Fixes the issue with missing audio element for remote participants
2016-07-15 13:18:47 -05:00
hristoterezov
52c6bbe731 Fixes typo 2016-07-15 13:14:10 -05:00
hristoterezov
1963972f75 Fixes the issue with missing audio element for remote participants 2016-07-15 13:12:14 -05:00
Michael Bell
85d0c62c1d Change reference to lib-jitsi-meet to use full github url
This resolves issue #694
2016-07-14 11:23:47 -04:00
Дамян Минков
dfa9bab9e1 Merge pull request #724 from jitsi/reloads
Implements support for conference reloads
2016-07-13 13:19:06 -05:00
champtar
3ed1532f25 Commit from translate.jitsi.org by user champtar.: 249 of 249 strings translated (0 fuzzy). 2016-07-13 11:01:42 +00:00
ibauersachs
115420db82 Commit from translate.jitsi.org by user ibauersachs.: 249 of 249 strings translated (0 fuzzy). 2016-07-12 16:39:53 +00:00
bgrozev
d2a6c4a97f Merge pull request #719 from jitsi/jibri-retries
Jibri retries
2016-07-11 15:53:25 -05:00
hristoterezov
c04874b087 Merge pull request #728 from jitsi/jwt-login-service-2
Jwt login service 2
2016-07-11 15:12:40 -05:00
Дамян Минков
a0b3018ea0 Merge pull request #729 from jitsi/issue_716
Fixes issue #716
2016-07-11 12:46:36 -05:00
hristoterezov
e46d45adea Fixes issue #716 2016-07-11 11:57:24 -05:00
paweldomas
f977030bd6 Add support for JWT login service 2016-07-11 13:47:10 +02:00
paweldomas
38fc1c01d4 Move XMPP login prompt handling to AuthHandler 2016-07-11 13:47:02 +02:00
hristoterezov
2f202deedf Merge branch 'master' into reloads 2016-07-08 16:03:10 -05:00
Дамян Минков
08bd40bb26 Merge pull request #722 from bgrozev/logging
Logs resolution changes to callstats.
2016-07-08 15:50:14 -05:00
Дамян Минков
28700173a0 Merge pull request #723 from bgrozev/doc
Adds documentation.
2016-07-08 15:49:48 -05:00
Boris Grozev
66a46fc580 Adds documentation. 2016-07-08 15:17:28 -05:00
Boris Grozev
de41977c77 Logs resolution changes to callstats. 2016-07-08 15:16:27 -05:00
hristoterezov
45c420561a Merge pull request #721 from jitsi/match-jquery-ui-version
Match the exact version of jquery-ui.
2016-07-08 14:43:22 -05:00
damencho
e240b15d61 Match the exact version of jquery-ui.
Using "compatible version" as ^... matches latest version 1.12.0 and not 1.10.5 (matches >=1.10.5 < 2.0.0) and this prevents it building from source with latest nodejs on clean environment.
2016-07-08 14:41:25 -05:00
bgrozev
486058834e Merge pull request #720 from jitsi/make-update
Executes npm update before making.
2016-07-08 10:54:45 -05:00
damencho
3e473ea9d7 Executes npm update before making.
Executes npm update before making, in order to update latest version of packages like lib-jitsi-meet which are updated from git. The npm install method is supposed to only resolve dependencies and not update to latest versions.
2016-07-08 10:52:04 -05:00
paweldomas
62dd54ab31 Display spinner for RETRYING recording status 2016-07-08 14:53:00 +02:00
paweldomas
b3e02add3d Fix moveToCorner method
If the class was contained the old code was removing it
2016-07-08 14:51:15 +02:00
hristoterezov
7bf9a82f0b Implements conference reload support 2016-07-07 20:44:04 -05:00
bgrozev
ce5ff20d5b Merge pull request #718 from jitsi/dialogs-update
Dialogs update
2016-07-06 14:52:45 -05:00
damencho
615daa8c9f Updates close function parameters. 2016-07-06 13:52:59 -05:00
damencho
5dffddceec Make sure we have only one dialog instance. 2016-07-06 13:26:27 -05:00
damencho
d5de49b5cf Returns the dialog instances that were created and adds an optional close callback. 2016-07-06 13:10:45 -05:00
damencho
62f7553ba4 Updates two button dialogs to be only single instance. 2016-07-06 11:00:04 -05:00
hristoterezov
b7ad6b606a Merge pull request #717 from tsareg/error_types
Expose JitsiTrackError through JitsiMeetJS.errorTypes.JitsiTrackError
2016-07-06 09:17:40 -05:00
tsareg
60c2ee41e3 Expose JitsiTrackError through JitsiMeetJS.errorTypes.JitsiTrackError 2016-07-06 16:21:26 +03:00
damencho
64475143cf Removes keyring dependency to switch to new build machine. 2016-06-30 15:31:35 -05:00
damencho
c1122eae3a Adds dependency to new keyring and new repo update. 2016-06-30 15:22:22 -05:00
Дамян Минков
1792b1ed85 Merge pull request #711 from jitsi/room_name_undefined
Fixes issue with room name parameter = undefined
2016-06-28 11:52:45 -05:00
hristoterezov
d624f2584d Fixes issue with room name parameter = undefined 2016-06-28 11:21:37 -05:00
27 changed files with 487 additions and 142 deletions

View File

@@ -3,7 +3,7 @@ BROWSERIFY = ./node_modules/.bin/browserify
UGLIFYJS = ./node_modules/.bin/uglifyjs
EXORCIST = ./node_modules/.bin/exorcist
CLEANCSS = ./node_modules/.bin/cleancss
CSS_FILES = font.css toastr.css main.css overlay.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css jquery.contextMenu.css keyboard-shortcuts.css
CSS_FILES = font.css toastr.css main.css overlay.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css recording.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css jquery.contextMenu.css keyboard-shortcuts.css
DEPLOY_DIR = libs
BROWSERIFY_FLAGS = -d
OUTPUT_DIR = .
@@ -13,7 +13,7 @@ IFRAME_API_DIR = ./modules/API/external
all: update-deps compile compile-iframe-api uglify uglify-iframe-api deploy clean
update-deps:
$(NPM) install
$(NPM) update
compile:
$(BROWSERIFY) $(BROWSERIFY_FLAGS) -e app.js -s APP | $(EXORCIST) $(OUTPUT_DIR)/app.bundle.js.map > $(OUTPUT_DIR)/app.bundle.js

View File

@@ -66,6 +66,7 @@ npm link lib-jitsi-meet
```
So now after changes in local `lib-jitsi-meet` repository you can rebuild it with `npm run install` and your `jitsi-meet` repository will use that modified library.
Note: when using node version 4.x, the make file of jitsi-meet do npm update which will delete the link, no longer the case with version 6.x.
If you do not want to use local repository anymore you should run
```bash

View File

@@ -317,7 +317,7 @@ function changeLocalDisplayName(nickname = '') {
APP.settings.setDisplayName(nickname);
room.setDisplayName(nickname);
APP.UI.changeDisplayName(APP.conference.localId, nickname);
APP.UI.changeDisplayName(APP.conference.getMyUserId(), nickname);
}
class ConferenceConnector {
@@ -410,6 +410,9 @@ class ConferenceConnector {
connection.disconnect();
APP.UI.notifyMaxUsersLimitReached();
break;
case ConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS:
window.location.reload();
break;
default:
this._handleConferenceFailed(err, ...params);
}
@@ -447,7 +450,6 @@ class ConferenceConnector {
}
export default {
localId: undefined,
isModerator: false,
audioMuted: false,
videoMuted: false,
@@ -530,7 +532,7 @@ export default {
* @returns {boolean}
*/
isLocalId (id) {
return this.localId === id;
return this.getMyUserId() === id;
},
/**
* Simulates toolbar button click for audio mute. Used by shortcuts and API.
@@ -728,7 +730,6 @@ export default {
_createRoom (localTracks) {
room = connection.initJitsiConference(APP.conference.roomName,
this._getConferenceOptions());
this.localId = room.myUserId();
this._setLocalAudioVideoStreams(localTracks);
roomLocker = createRoomLocker(room);
this._room = room; // FIXME do not use this
@@ -812,7 +813,7 @@ export default {
this.isSharingScreen = false;
}
APP.UI.setVideoMuted(this.localId, this.videoMuted);
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
APP.UI.updateDesktopSharingButtons();
});
@@ -847,7 +848,7 @@ export default {
}
APP.UI.enableMicrophoneButton();
APP.UI.setAudioMuted(this.localId, this.audioMuted);
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
});
},
@@ -1009,7 +1010,7 @@ export default {
let id;
const mute = track.isMuted();
if(track.isLocal()){
id = this.localId;
id = APP.conference.getMyUserId();
if(track.getType() === "audio") {
this.audioMuted = mute;
} else {
@@ -1231,6 +1232,12 @@ export default {
room.dial(sipNumber);
});
APP.UI.addListener(UIEvents.RESOLUTION_CHANGED,
(id, oldResolution, newResolution, delay) => {
room.sendApplicationLog("Resolution change id=" + id
+ " old=" + oldResolution + " new=" + newResolution
+ " delay=" + delay);
});
// Starts or stops the recording for the conference.
APP.UI.addListener(UIEvents.RECORDING_TOGGLED, (options) => {
@@ -1513,4 +1520,4 @@ export default {
APP.UI.setLocalRaisedHandStatus(raisedHand);
}
}
};
};

View File

@@ -1,6 +1,5 @@
/* global APP, JitsiMeetJS, config */
//FIXME:
import LoginDialog from './modules/UI/authentication/LoginDialog';
import AuthHandler from './modules/UI/authentication/AuthHandler';
const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection;
@@ -92,33 +91,6 @@ function connect(id, password, roomName) {
});
}
/**
* Show Authentication Dialog and try to connect with new credentials.
* If failed to connect because of PASSWORD_REQUIRED error
* then ask for password again.
* @param {string} [roomName]
* @returns {Promise<JitsiConnection>}
*/
function requestAuth(roomName) {
return new Promise(function (resolve, reject) {
let authDialog = LoginDialog.showAuthDialog(
function (id, password) {
connect(id, password, roomName).then(function (connection) {
authDialog.close();
resolve(connection);
}, function (err) {
if (err === ConnectionErrors.PASSWORD_REQUIRED) {
authDialog.displayError(err);
} else {
authDialog.close();
reject(err);
}
});
}
);
});
}
/**
* Open JitsiConnection using provided credentials.
* If retry option is true it will show auth dialog on PASSWORD_REQUIRED error.
@@ -157,7 +129,7 @@ export function openConnection({id, password, retry, roomName}) {
if (config.token) {
throw err;
} else {
return requestAuth(roomName);
return AuthHandler.requestAuth(roomName, connect);
}
} else {
throw err;

4
css/recording.css Normal file
View File

@@ -0,0 +1,4 @@
.recordingSpinner {
display: none;
vertical-align: text-bottom;
}

2
images/spin.svg Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width='20px' height='20px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-spin"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><g transform="translate(50 50)"><g transform="rotate(0) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(45) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.12s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.12s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(90) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.25s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.25s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(135) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.37s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.37s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(180) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.5s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.5s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(225) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.62s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.62s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(270) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.75s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.75s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g><g transform="rotate(315) translate(34 0)"><circle cx="0" cy="0" r="8" fill="#ffffff"><animate attributeName="opacity" from="1" to="0.1" begin="0.87s" dur="1s" repeatCount="indefinite"></animate><animateTransform attributeName="transform" type="scale" from="1.5" to="1" begin="0.87s" dur="1s" repeatCount="indefinite"></animateTransform></circle></g></g></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -174,7 +174,10 @@
</div>
<span id="videoConnectionMessage"></span>
<span id="videoResolutionLabel">HD</span>
<span id="recordingLabel" class="centeredVideoLabel"></span>
<span id="recordingLabel" class="centeredVideoLabel">
<span id="recordingLabelText"></span>
<img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
</span>
</div>
<div id="remoteVideos">

View File

@@ -2,10 +2,13 @@
"en": "Englisch",
"bg": "Bulgarisch",
"de": "Deutsch",
"tr": "Türkisch",
"it": "Italienisch",
"es": "Spanisch",
"fr": "Französisch",
"sl": "Slowenisch",
"hy": "Armenisch",
"it": "Italienisch",
"oc": "Okzitanisch",
"sk": "Slowakisch",
"sv": "Schwedisch"
"sl": "Slowenisch",
"sv": "Schwedisch",
"tr": "Türkisch"
}

View File

@@ -2,10 +2,13 @@
"en": "Anglais",
"bg": "Bulgare",
"de": "Allemand",
"tr": "Turc",
"it": "Italien",
"es": "Espagnol",
"fr": "Français",
"sl": "Slovène",
"hy": "Arménien",
"it": "Italien",
"oc": "Occitan",
"sk": "Slovaque",
"sv": "Suédois"
"sl": "Slovène",
"sv": "Suédois",
"tr": "Turc"
}

View File

@@ -8,9 +8,20 @@
"participant": "Teilnehmer",
"me": "ich",
"speaker": "Sprecher",
"raisedHand": "Möchte sprechen",
"defaultNickname": "Bsp: Heidi Blau",
"defaultLink": "Bsp.: __url__",
"calling": "Rufe __name__ an...",
"userMedia": {
"react-nativeGrantPermissions": "",
"chromeGrantPermissions": "",
"androidGrantPermissions": "",
"firefoxGrantPermissions": "Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons durch anwählen von <b><i>Ausgewähltes Gerät freigeben</i></b>",
"operaGrantPermissions": "Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons durch anwählen von <b><i>Erlauben</i></b>",
"iexplorerGrantPermissions": "",
"safariGrantPermissions": "Bitte Berechtigung zur Verwendung der Kamera und des Mikrofons durch anwählen von <b><i>OK</i></b> erteilen",
"nwjsGrantPermissions": "Bitte Berechtigungen zur Verwendung der Kamera und des Mikrofons erteilen"
},
"keyboardShortcuts": {
"keyboardShortcuts": "Tastaturkürzel:",
"raiseHand": "Heben Sie Ihre Hand.",
@@ -150,7 +161,8 @@
"grantedTo": "Moderatorenrechte an __to__ vergeben.",
"grantedToUnknown": "Moderatorenrechte an $t(somebody) vergeben.",
"muted": "Der Konferenz wurde stumm beigetreten.",
"mutedTitle": "Stummschaltung aktiv!"
"mutedTitle": "Stummschaltung aktiv!",
"raisedHand": "Möchte sprechen."
},
"dialog": {
"kickMessage": "Oh! Sie wurden aus der Konferenz ausgeschlossen.",
@@ -240,11 +252,11 @@
"cameraErrorPresent": "Fehler beim Verbinden zur Kamera.",
"cameraUnsupportedResolutionError": "Die Kamera unterstützt die erforderliche Auflösung nicht.",
"cameraUnknownError": "Die Kamera kann aus einem unbekannten Grund nicht verwendet werden.",
"cameraPermissionDeniedError": "Sie haben die Berechtigung für die Benutzung der Kamera nicht erteilt.",
"cameraNotFoundError": "Die angeforderte Kamera konnte nicht gefunden werden.",
"cameraPermissionDeniedError": "Die Berechtigung zur Verwendung der Kamera wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht sehen. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste um die Berechtigungen zu erteilen.",
"cameraNotFoundError": "Die Berechtigung zur Verwendung der Kamera wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht sehen. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste um die Berechtigungen zu erteilen.",
"cameraConstraintFailedError": "Ihre Kamera erfüllt die notwendigen Anforderungen nicht.",
"micUnknownError": "Das Mikrofon kann aus einem unbekannten Grund nicht verwendet werden.",
"micPermissionDeniedError": "Sie haben die Berechtigung für die Benutzung des Mikrofons nicht erteilt.",
"micPermissionDeniedError": "Die Berechtigung zur Verwendung des Mikrofons wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht hören. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste um die Berechtigungen zu erteilen.",
"micNotFoundError": "Das angeforderte Mikrofon konnte nicht gefunden werden.",
"micConstraintFailedError": "Ihr Mikrofon erfüllt die notwendigen Anforderungen nicht."
},

View File

@@ -8,8 +8,33 @@
"participant": "Participant",
"me": "moi",
"speaker": "Haut-parleur",
"raisedHand": "Aimerais prendre la parole",
"defaultNickname": "ex. Jean Dupont",
"defaultLink": "ex. __url__",
"calling": "Appel __nom__ ...",
"userMedia": {
"react-nativeGrantPermissions": "",
"chromeGrantPermissions": "",
"androidGrantPermissions": "",
"firefoxGrantPermissions": "Merci d'accorder l'autorisation d'utiliser votre caméra et un microphone en appuyant sur le bouton<b> <i> Partager le périphérique sélectionné </ i> </ b> ",
"operaGrantPermissions": "Merci d'autoriser le partage de votre camera et microphone en appuyant sur le bouton <b><i>Autoriser</i></b>",
"iexplorerGrantPermissions": "",
"safariGrantPermissions": "Merci d'autoriser l'utilisation de votre caméra et du microphone en appuyant sur <b><i>OK</i></b>",
"nwjsGrantPermissions": "Merci d'autoriser le partage de votre camera et microphone"
},
"keyboardShortcuts": {
"keyboardShortcuts": "Raccourcis clavier :",
"raiseHand": "Demander la parole.",
"pushToTalk": "Appuyer pour parler.",
"toggleScreensharing": "Basculer entre la caméra et le partage d'écran.",
"toggleFilmstrip": "Afficher ou masquer les vidéos miniatures.",
"toggleShortcuts": "Afficher ou masquer ce menu d'aide.",
"focusLocal": "Focus sur la vidéo locale.",
"focusRemote": "Focus sur l'une des vidéos distantes.",
"toggleChat": "Ouvrir ou fermer le panneau de conversation.",
"mute": "Activer ou désactiver le microphone.",
"videoMute": "Arrêter ou démarrer la vidéo locale."
},
"welcomepage": {
"go": "Créer",
"roomname": "Saisissez un nom de salle",
@@ -66,7 +91,9 @@
"dialpad": "Afficher le clavier de numérotation",
"sharedVideoMutedPopup": "Votre vidéo a été mise en muet<br/>pour que vous puissiez parler aux autres participants.",
"micMutedPopup": "Votre microphone a été désactivé afin que vous<br/>puissiez profiter pleinement de votre vidéo partagée.",
"unableToUnmutePopup": "Vous ne pouvez pas réactiver votre microphone pendant que la vidéo partagée est activée."
"unableToUnmutePopup": "Vous ne pouvez pas réactiver votre microphone pendant que la vidéo partagée est activée.",
"cameraDisabled": "La camera n'est pas disponible",
"micDisabled": "Le microphone n'est pas disponible"
},
"bottomtoolbar": {
"chat": "Ouvrir / fermer le chat",
@@ -88,7 +115,11 @@
"startVideoMuted": "Démarrer sans vidéo",
"selectCamera": "Sélectionnez une caméra",
"selectMic": "Sélectionnez un microphone",
"followMe": "Activer \"me suivre\""
"selectAudioOutput": "Sélectionner la sortie audio",
"followMe": "Activer \"me suivre\"",
"noDevice": "Aucun",
"noPermission": "L'autorisation d'utiliser l'appareil n'a pas été accordé",
"avatarUrl": "URL de l'avatar"
},
"videothumbnail": {
"editnickname": "Cliquez pour modifier<br/>votre nom",
@@ -97,7 +128,8 @@
"mute": "Un participant a coupé son micro",
"kick": "Exclure",
"muted": "Muet",
"domute": "Couper le son"
"domute": "Couper le son",
"flip": "Retourner"
},
"connectionindicator": {
"bitrate": "Débit :",
@@ -129,7 +161,8 @@
"grantedTo": "Droits modérateur accordés à __to__ !",
"grantedToUnknown": "Droits modérateur accordés à $t(somebody) !",
"muted": "Vous avez commencé la conversation en muet.",
"mutedTitle": "Vous êtes en muet !"
"mutedTitle": "Vous êtes en muet !",
"raisedHand": "Aimerais prendre la parole."
},
"dialog": {
"kickMessage": "Oups! Vous avez été renvoyé de la réunion !",
@@ -177,7 +210,7 @@
"joinAgain": "Rejoignez à nouveau la conférence",
"Share": "Partager",
"Save": "Sauvegarder",
"recording": "",
"recording": "Enregistrement",
"recordingToken": "Saisissez un jeton d'enregistrement",
"Dial": "Composer",
"sipMsg": "Saisissez un numéro SIP",
@@ -205,13 +238,27 @@
"feedbackQuestion": "Comment était votre conférence ?",
"thankYou": "Merci d'avoir utilisé __appName__ !",
"sorryFeedback": "Nous sommes désolés d'apprendre cela. Voulez-vous nous en dire plus ?",
"liveStreaming": "",
"liveStreaming": "Direct",
"streamKey": "Stream name/key",
"startLiveStreaming": "Commencer le direct",
"stopStreamingWarning": "Désirez-vous vraiment arrêter le direct?",
"stopRecordingWarning": "Désirez-vous vraiment arrêter l'enregistrement?",
"stopLiveStreaming": "Arrêter le direct",
"stopRecording": "Arrêter l'enregistrement"
"stopRecording": "Arrêter l'enregistrement",
"doNotShowWarningAgain": "Ne plus afficher cet avertissement",
"permissionDenied": "Permission refusée",
"screenSharingPermissionDeniedError": "Vous n'avez pas autorisé le partage de votre écran.",
"micErrorPresent": "Une erreur est survenue lors de la connexion à votre microphone.",
"cameraErrorPresent": "Votre caméra ne satisfait pas certaines des contraintes nécessaires.",
"cameraUnsupportedResolutionError": "Votre appareil ne prend pas en charge la résolution vidéo requise.",
"cameraUnknownError": "Vous ne pouvez pas utiliser la caméra pour une raison inconnue.",
"cameraPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre caméra. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous voir. Utilisez le bouton de la caméra dans la barre d'adresse pour résoudre ce problème.",
"cameraNotFoundError": "La caméra demandée n'a pas été trouvée.",
"cameraConstraintFailedError": "Votre caméra ne satisfait pas certaines des contraintes nécessaires.",
"micUnknownError": "Vous ne pouvez pas utiliser le microphone pour une raison inconnue.",
"micPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre microphone. Vous pouvez toujours participer à la conférence, mais les autres ne vont pas vous entendre. Utilisez le bouton du microphone dans la barre d'adresse pour résoudre ce problème.",
"micNotFoundError": "Le microphone demandé n'a pas été trouvé.",
"micConstraintFailedError": "Votre microphone ne satisfait pas certaines des contraintes nécessaires."
},
"email": {
"sharedKey": [
@@ -258,7 +305,9 @@
"on": "Enregistrement",
"off": "Enregistrement arrêter",
"failedToStart": "L'enregistrement n'as pas réussi à démarrer",
"buttonTooltip": "Démarrer / arrêter l'enregistrement"
"buttonTooltip": "Démarrer / arrêter l'enregistrement",
"error": "Échec de l'enregistrement. Veuillez réessayer.",
"unavailable": "Le service d'enregistrement est actuellement indisponible. Veuillez réessayer plus tard."
},
"liveStreaming": {
"pending": "Commencer le direct...",
@@ -267,6 +316,8 @@
"unavailable": "Le direct est temporairement indisponible. Veuillez réessayer plus tard.",
"failedToStart": "Le direct n'as pas réussi à démarrer",
"buttonTooltip": "Démarrer / arrêter le direct",
"streamIdRequired": "Merci de renseigner le stream id pour lancer le direct."
"streamIdRequired": "Merci de renseigner le stream id pour lancer le direct.",
"error": "La retransmission en direct a échoué. Veuillez réessayer.",
"busy": "Tous les enregistreurs sont actuellement occupés. Veuillez réessayer plus tard."
}
}

View File

@@ -345,7 +345,7 @@ JitsiMeetExternalAPI.prototype.removeEventListeners = function(events) {
* Removes the listeners and removes the Jitsi Meet frame.
*/
JitsiMeetExternalAPI.prototype.dispose = function() {
this.postis.dispose();
this.postis.destroy();
var frame = document.getElementById(this.frameName);
if(frame)
frame.src = 'about:blank';

View File

@@ -272,14 +272,14 @@ UI.setRaisedHandStatus = (participant, raisedHandStatus) => {
* Sets the local "raised hand" status.
*/
UI.setLocalRaisedHandStatus = (raisedHandStatus) => {
VideoLayout.setRaisedHandStatus(APP.conference.localId, raisedHandStatus);
VideoLayout.setRaisedHandStatus(APP.conference.getMyUserId(), raisedHandStatus);
};
/**
* Initialize conference UI.
*/
UI.initConference = function () {
let id = APP.conference.localId;
let id = APP.conference.getMyUserId();
Toolbar.updateRoomUrl(window.location.href);
// Add myself to the contact list.
@@ -1210,9 +1210,9 @@ UI.showExtensionRequiredDialog = function (url) {
UI.showDeviceErrorDialog = function (micError, cameraError) {
let localStoragePropName = "doNotShowErrorAgain";
let isMicJitsiTrackErrorAndHasName = micError && micError.name &&
micError instanceof JitsiMeetJS.JitsiTrackError;
micError instanceof JitsiMeetJS.errorTypes.JitsiTrackError;
let isCameraJitsiTrackErrorAndHasName = cameraError && cameraError.name &&
cameraError instanceof JitsiMeetJS.JitsiTrackError;
cameraError instanceof JitsiMeetJS.errorTypes.JitsiTrackError;
let showDoNotShowWarning = false;
if (micError && cameraError && isMicJitsiTrackErrorAndHasName &&

View File

@@ -209,7 +209,7 @@ const AudioLevels = {
drawContext.drawImage(canvasCache, 0, 0);
if (id === LOCAL_LEVEL) {
id = APP.conference.localId;
id = APP.conference.getMyUserId();
if (!id) {
return;
}

View File

@@ -1,15 +1,20 @@
/* global JitsiMeetJS, APP */
/* global APP, config, JitsiMeetJS, Promise */
import LoginDialog from './LoginDialog';
import UIEvents from '../../../service/UI/UIEvents';
import UIUtil from '../util/UIUtil';
import {openConnection} from '../../../connection';
const ConferenceEvents = JitsiMeetJS.events.conference;
const ConnectionErrors = JitsiMeetJS.errors.connection;
let externalAuthWindow;
let authRequiredDialog;
let isTokenAuthEnabled
= typeof config.tokenAuthUrl === "string" && config.tokenAuthUrl.length;
let getTokenAuthUrl
= JitsiMeetJS.util.AuthUtil.getTokenAuthUrl.bind(null, config.tokenAuthUrl);
/**
* Authenticate using external service or just focus
* external auth window if there is one already.
@@ -23,19 +28,103 @@ function doExternalAuth (room, lockPassword) {
return;
}
if (room.isJoined()) {
room.getExternalAuthUrl(true).then(function (url) {
let getUrl;
if (isTokenAuthEnabled) {
getUrl = Promise.resolve(getTokenAuthUrl(room.getName(), true));
initJWTTokenListener(room);
} else {
getUrl = room.getExternalAuthUrl(true);
}
getUrl.then(function (url) {
externalAuthWindow = LoginDialog.showExternalAuthDialog(
url,
function () {
externalAuthWindow = null;
room.join(lockPassword);
if (!isTokenAuthEnabled) {
room.join(lockPassword);
}
}
);
});
} else {
// If conference has not been started yet
// then redirect to login page
room.getExternalAuthUrl().then(UIUtil.redirect);
if (isTokenAuthEnabled) {
redirectToTokenAuthService(room.getName());
} else {
room.getExternalAuthUrl().then(UIUtil.redirect);
}
}
}
/**
* Redirect the user to the token authentication service for the login to be
* 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.
*/
function redirectToTokenAuthService(roomName) {
UIUtil.redirect(getTokenAuthUrl(roomName, false));
}
/**
* 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 fo the conference room.
*/
function initJWTTokenListener(room) {
var self = this;
var listener = function (event) {
if (externalAuthWindow !== event.source) {
console.warn("Ignored message not coming " +
"from external authnetication window");
return;
}
if (event.data && event.data.jwtToken) {
config.token = event.data.jwtToken;
console.info("Received JWT token:", config.token);
var roomName = room.getName();
openConnection({retry: false, roomName: roomName })
.then(function (connection) {
// Start new connection
let newRoom = connection.initJitsiConference(
roomName, APP.conference._getConferenceOptions());
// Authenticate from the new connection to get
// the session-ID from the focus, which wil then be used
// to upgrade current connection's user role
newRoom.room.moderator.authenticate().then(function () {
connection.disconnect();
// At this point we'll have session-ID stored in
// the settings. It wil be used in the call below
// to upgrade user's role
room.room.moderator.authenticate()
.then(function () {
console.info("User role upgrade done !");
unregister();
}).catch(function (err, errCode) {
console.error(
"Authentication failed: ", err, errCode);
unregister();
}
);
}).catch(function (error, code) {
unregister();
connection.disconnect();
console.error(
'Authentication failed on the new connection',
error, code);
});
}, function (err) {
unregister();
console.error("Failed to open new connection", err);
});
}
};
var unregister = function () {
window.removeEventListener("message", listener);
};
if (window.addEventListener) {
window.addEventListener("message", listener, false);
}
}
@@ -52,7 +141,8 @@ function doXmppAuth (room, lockPassword) {
// (this will store sessionId in the localStorage)
// 3. close new connection
// 4. reallocate focus in current room
openConnection({id, password}).then(function (connection) {
openConnection({id, password, roomName: room.getName()}).then(
function (connection) {
// open room
let newRoom = connection.initJitsiConference(
room.getName(), APP.conference._getConferenceOptions()
@@ -99,7 +189,7 @@ function doXmppAuth (room, lockPassword) {
* @param {string} [lockPassword] password to use if the conference is locked
*/
function authenticate (room, lockPassword) {
if (room.isExternalAuthEnabled()) {
if (isTokenAuthEnabled || room.isExternalAuthEnabled()) {
doExternalAuth(room, lockPassword);
} else {
doXmppAuth(room, lockPassword);
@@ -156,10 +246,52 @@ function closeAuth() {
}
}
function showXmppPasswordPrompt(roomName, connect) {
return new Promise(function (resolve, reject) {
let authDialog = LoginDialog.showAuthDialog(
function (id, password) {
connect(id, password, roomName).then(function (connection) {
authDialog.close();
resolve(connection);
}, function (err) {
if (err === ConnectionErrors.PASSWORD_REQUIRED) {
authDialog.displayError(err);
} else {
authDialog.close();
reject(err);
}
});
}
);
});
}
/**
* Show Authentication Dialog and try to connect with new credentials.
* If failed to connect because of PASSWORD_REQUIRED error
* then ask for password again.
* @param {string} [roomName] name of the conference room
* @param {function(id, password, roomName)} [connect] function that returns
* a Promise which resolves with JitsiConnection or fails with one of
* ConnectionErrors.
* @returns {Promise<JitsiConnection>}
*/
function requestAuth(roomName, connect) {
if (isTokenAuthEnabled) {
// This Promise never resolves as user gets redirected to another URL
return new Promise(function (resolve, reject) {
redirectToTokenAuthService(roomName);
});
} else {
return showXmppPasswordPrompt(roomName, connect);
}
}
export default {
authenticate,
requireAuth,
requestAuth,
closeAuth,
logout
};

View File

@@ -116,6 +116,7 @@ const ConferenceErrors = JitsiMeetJS.errors.conference;
*/
export default function createRoomLocker (room) {
let password;
let dialog = null;
function lock (newPass) {
return room.lock(newPass).then(function () {
@@ -196,12 +197,21 @@ export default function createRoomLocker (room) {
* Show notification that to set/remove password user must be moderator.
*/
notifyModeratorRequired () {
if (dialog)
return;
let closeCallback = function () {
dialog = null;
};
if (password) {
APP.UI.messageHandler
.openMessageDialog(null, "dialog.passwordError");
dialog = APP.UI.messageHandler
.openMessageDialog(null, "dialog.passwordError",
null, null, closeCallback);
} else {
APP.UI.messageHandler
.openMessageDialog(null, "dialog.passwordError2");
dialog = APP.UI.messageHandler
.openMessageDialog(null, "dialog.passwordError2",
null, null, closeCallback);
}
}
};

View File

@@ -21,6 +21,10 @@ import Feedback from '../Feedback.js';
import Toolbar from '../toolbars/Toolbar';
import BottomToolbar from '../toolbars/BottomToolbar';
/**
* The dialog for user input.
*/
let dialog = null;
/**
* Indicates if the recording button should be enabled.
@@ -50,7 +54,7 @@ function _requestLiveStreamId() {
"liveStreaming.streamIdRequired");
return new Promise(function (resolve, reject) {
let dialog = APP.UI.messageHandler.openDialogWithStates({
dialog = APP.UI.messageHandler.openDialogWithStates({
state0: {
html:
`<h2>${msg}</h2>
@@ -104,6 +108,10 @@ function _requestLiveStreamId() {
}
}
}
}, {
close: function () {
dialog = null;
}
});
});
}
@@ -117,7 +125,7 @@ function _requestRecordingToken () {
let token = APP.translation.translateString("dialog.token");
return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog(
dialog = APP.UI.messageHandler.openTwoButtonDialog(
null, null, null,
`<h2>${msg}</h2>
<input name="recordingToken" type="text"
@@ -132,7 +140,9 @@ function _requestRecordingToken () {
}
},
null,
function () { },
function () {
dialog = null;
},
':input:first'
);
});
@@ -161,7 +171,7 @@ function _showStopRecordingPrompt (recordingType) {
}
return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog(
dialog = APP.UI.messageHandler.openTwoButtonDialog(
title,
null,
message,
@@ -174,6 +184,10 @@ function _showStopRecordingPrompt (recordingType) {
} else {
reject();
}
},
null,
function () {
dialog = null;
}
);
});
@@ -187,10 +201,11 @@ function _showStopRecordingPrompt (recordingType) {
*/
function moveToCorner(selector, move) {
let moveToCornerClass = "moveToCorner";
let containsClass = selector.hasClass(moveToCornerClass);
if (move && !selector.hasClass(moveToCornerClass))
if (move && !containsClass)
selector.addClass(moveToCornerClass);
else
else if (!move && containsClass)
selector.removeClass(moveToCornerClass);
}
@@ -206,11 +221,21 @@ var Status = {
AVAILABLE: "available",
UNAVAILABLE: "unavailable",
PENDING: "pending",
RETRYING: "retrying",
ERROR: "error",
FAILED: "failed",
BUSY: "busy"
};
/**
* Checks whether if the given status is either PENDING or RETRYING
* @param status {Status} Jibri status to be checked
* @returns {boolean} true if the condition is met or false otherwise.
*/
function isStartingStatus(status) {
return status === Status.PENDING || status === Status.RETRYING;
}
/**
* Manages the recording user interface and user experience.
* @type {{init, initRecordingButton, showRecordingButton, updateRecordingState,
@@ -231,7 +256,7 @@ var Recording = {
// everyone.
if (config.iAmRecorder) {
VideoLayout.enableDeviceAvailabilityIcons(
APP.conference.localId, false);
APP.conference.getMyUserId(), false);
VideoLayout.setLocalVideoVisible(false);
Feedback.enableFeedback(false);
Toolbar.enable(false);
@@ -279,11 +304,16 @@ var Recording = {
var self = this;
selector.click(function () {
if (dialog)
return;
switch (self.currentState) {
case Status.ON:
case Status.RETRYING:
case Status.PENDING: {
_showStopRecordingPrompt(recordingType).then(() =>
self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED));
self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED),
() => {});
break;
}
case Status.AVAILABLE:
@@ -318,16 +348,24 @@ var Recording = {
break;
}
case Status.BUSY: {
APP.UI.messageHandler.openMessageDialog(
dialog = APP.UI.messageHandler.openMessageDialog(
self.recordingTitle,
self.recordingBusy
self.recordingBusy,
null, null,
function () {
dialog = null;
}
);
break;
}
default: {
APP.UI.messageHandler.openMessageDialog(
dialog = APP.UI.messageHandler.openMessageDialog(
self.recordingTitle,
self.recordingUnavailable
self.recordingUnavailable,
null, null,
function () {
dialog = null;
}
);
}
}
@@ -373,7 +411,8 @@ var Recording = {
this.currentState = recordingState;
// TODO: handle recording state=available
if (recordingState === Status.ON) {
if (recordingState === Status.ON ||
recordingState === Status.RETRYING) {
buttonSelector.removeClass(this.baseClass);
buttonSelector.addClass(this.baseClass + " active");
@@ -388,14 +427,14 @@ var Recording = {
// We don't want to do any changes if this is
// an availability change.
if (oldState !== Status.ON
&& oldState !== Status.PENDING)
&& !isStartingStatus(oldState))
return;
buttonSelector.removeClass(this.baseClass + " active");
buttonSelector.addClass(this.baseClass);
let messageKey;
if (oldState === Status.PENDING)
if (isStartingStatus(oldState))
messageKey = this.failedToStartKey;
else
messageKey = this.recordingOffKey;
@@ -427,6 +466,12 @@ var Recording = {
if (recordingState !== Status.AVAILABLE
&& !labelSelector.is(":visible"))
labelSelector.css({display: "inline-block"});
// Recording spinner
if (recordingState === Status.RETRYING)
$("#recordingSpinner").show();
else
$("#recordingSpinner").hide();
},
// checks whether recording is enabled and whether we have params
// to start automatically recording
@@ -445,11 +490,12 @@ var Recording = {
*/
_updateStatusLabel(textKey, isCentered) {
let labelSelector = $('#recordingLabel');
let labelTextSelector = $('#recordingLabelText');
moveToCorner(labelSelector, !isCentered);
labelSelector.attr("data-i18n", textKey);
labelSelector.text(APP.translation.translateString(textKey));
labelTextSelector.attr("data-i18n", textKey);
labelTextSelector.text(APP.translation.translateString(textKey));
}
};

View File

@@ -17,6 +17,13 @@ export const SHARED_VIDEO_CONTAINER_TYPE = "sharedvideo";
*/
const defaultSharedVideoLink = "https://www.youtube.com/watch?v=xNXN7CZk8X0";
const updateInterval = 5000; // milliseconds
/**
* The dialog for user input (video link).
* @type {null}
*/
let dialog = null;
/**
* Manager of shared video.
*/
@@ -56,11 +63,14 @@ export default class SharedVideoManager {
* asks whether the user wants to stop sharing the video.
*/
toggleSharedVideo () {
if (dialog)
return;
if(!this.isSharedVideoShown) {
requestVideoLink().then(
url => this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, url, 'start'),
err => console.error('SHARED VIDEO CANCELED', err)
err => console.log('SHARED VIDEO CANCELED', err)
);
return;
}
@@ -68,11 +78,16 @@ export default class SharedVideoManager {
if(APP.conference.isLocalId(this.from)) {
showStopVideoPropmpt().then(() =>
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop'));
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop'),
() => {});
} else {
APP.UI.messageHandler.openMessageDialog(
dialog = APP.UI.messageHandler.openMessageDialog(
"dialog.shareVideoTitle",
"dialog.alreadySharedVideoMsg"
"dialog.alreadySharedVideoMsg",
null, null,
function () {
dialog = null;
}
);
}
}
@@ -624,7 +639,6 @@ SharedVideoThumb.prototype.createContainer = function (spanId) {
// add the avatar
var avatar = document.createElement('img');
avatar.id = 'avatar_' + this.id;
avatar.className = 'sharedVideoAvatar';
avatar.src = "https://img.youtube.com/vi/" + this.url + "/0.jpg";
container.appendChild(avatar);
@@ -700,7 +714,7 @@ function getYoutubeLink(url) {
*/
function showStopVideoPropmpt() {
return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog(
dialog = APP.UI.messageHandler.openTwoButtonDialog(
"dialog.removeSharedVideoTitle",
null,
"dialog.removeSharedVideoMsg",
@@ -713,6 +727,10 @@ function showStopVideoPropmpt() {
} else {
reject();
}
},
null,
function () {
dialog = null;
}
);
@@ -735,7 +753,7 @@ function requestVideoLink() {
const defaultUrl = i18n.translateString("defaultLink", i18nOptions);
return new Promise(function (resolve, reject) {
let dialog = APP.UI.messageHandler.openDialogWithStates({
dialog = APP.UI.messageHandler.openDialogWithStates({
state0: {
html: `
<h2>${title}</h2>
@@ -795,8 +813,11 @@ function requestVideoLink() {
}
}
}
}, {
close: function () {
dialog = null;
}
});
});
}

View File

@@ -156,7 +156,7 @@ var ContactList = {
if(!displayName)
return;
if (id === 'localVideoContainer') {
id = APP.conference.localId;
id = APP.conference.getMyUserId();
}
let contactName = $(`#contacts #${id}>p`);

View File

@@ -15,6 +15,12 @@ let notificationsEnabled = true;
*/
let popupEnabled = true;
/**
* Currently displayed two button dialog.
* @type {null}
*/
let twoButtonDialog = null;
var messageHandler = {
OK: "dialog.OK",
CANCEL: "dialog.Cancel",
@@ -30,10 +36,14 @@ var messageHandler = {
* titleKey will be used to get a title via the translation API.
* @param message the message to show. If a falsy value is provided,
* messageKey will be used to get a message via the translation API.
* @param closeFunction function to be called after
* the prompt is closed (optional)
* @return the prompt that was created, or null
*/
openMessageDialog: function(titleKey, messageKey, title, message) {
openMessageDialog: function(titleKey, messageKey, title, message,
closeFunction) {
if (!popupEnabled)
return;
return null;
if (!title) {
title = APP.translation.generateTranslationHTML(titleKey);
@@ -42,9 +52,14 @@ var messageHandler = {
message = APP.translation.generateTranslationHTML(messageKey);
}
$.prompt(message,
{title: title, persistent: false}
);
return $.prompt(message, {
title: title,
persistent: false,
close: function (e, v, m, f) {
if(closeFunction)
closeFunction(e, v, m, f);
}
});
},
/**
* Shows a message to the user with two buttons: first is given as a
@@ -63,13 +78,14 @@ var messageHandler = {
* the dialog is opened
* @param defaultButton index of default button which will be activated when
* the user press 'enter'. Indexed from 0.
* @return the prompt that was created, or null
*/
openTwoButtonDialog: function(titleKey, titleString, msgKey, msgString,
persistent, leftButtonKey, submitFunction, loadedFunction,
closeFunction, focus, defaultButton) {
if (!popupEnabled)
return;
if (!popupEnabled || twoButtonDialog)
return null;
var buttons = [];
@@ -87,16 +103,25 @@ var messageHandler = {
if (msgKey) {
message = APP.translation.generateTranslationHTML(msgKey);
}
$.prompt(message, {
twoButtonDialog = $.prompt(message, {
title: title,
persistent: false,
buttons: buttons,
defaultButton: defaultButton,
focus: focus,
loaded: loadedFunction,
submit: submitFunction,
close: closeFunction
submit: function (e, v, m, f) {
twoButtonDialog = null;
if (submitFunction)
submitFunction(e, v, m, f);
},
close: function (e, v, m, f) {
twoButtonDialog = null;
if (closeFunction)
closeFunction(e, v, m, f);
}
});
return twoButtonDialog;
},
/**
@@ -133,7 +158,7 @@ var messageHandler = {
if (persistent) {
args.closeText = '';
}
return new Impromptu(msgString, args);
},

View File

@@ -21,7 +21,7 @@ function getStreamOwnerId(stream) {
return;
}
if (stream.isLocal()) { // local stream doesn't have method "getParticipantId"
return APP.conference.localId;
return APP.conference.getMyUserId();
} else {
return stream.getParticipantId();
}

View File

@@ -18,7 +18,7 @@ function LocalVideo(VideoLayout, emitter) {
this.emitter = emitter;
Object.defineProperty(this, 'id', {
get: function () {
return APP.conference.localId;
return APP.conference.getMyUserId();
}
});
SmallVideo.call(this, VideoLayout);

View File

@@ -144,13 +144,7 @@ RemoteVideo.prototype.removeRemoteStreamElement = function (stream) {
var isVideo = stream.isVideoTrack();
var elementID = SmallVideo.getStreamElementID(stream);
var select = null;
if (isVideo) {
select = $('#' + elementID);
}
else
select = $('#' + this.videoSpanId + '>audio');
var select = $('#' + elementID);
select.remove();
console.info((isVideo ? "Video" : "Audio") +

View File

@@ -2,6 +2,7 @@
/* jshint -W101 */
import Avatar from "../avatar/Avatar";
import UIUtil from "../util/UIUtil";
import UIEvents from "../../../service/UI/UIEvents";
const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper;
@@ -299,9 +300,6 @@ SmallVideo.prototype.updateIconPositions = function () {
/**
* Creates the element indicating the moderator(owner) of the conference.
*
* @param parentElement the parent element where the owner indicator will
* be added
*/
SmallVideo.prototype.createModeratorIndicatorElement = function () {
// Show moderator indicator
@@ -329,6 +327,23 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
APP.translation.translateElement($('#' + this.videoSpanId + ' .focusindicator'));
};
/**
* Removes the element indicating the moderator(owner) of the conference.
*/
SmallVideo.prototype.removeModeratorIndicatorElement = function () {
$('#' + this.videoSpanId + ' .focusindicator').remove();
};
/**
* This is an especially interesting function. A naive reader might think that
* it returns this SmallVideo's "video" element. But it is much more exciting.
* It first finds this video's parent element using jquery, then uses a utility
* from lib-jitsi-meet to extract the video element from it (with two more
* jquery calls), and finally uses jquery again to encapsulate the video element
* in an array. This last step allows (some might prefer "forces") users of
* this function to access the video element via the 0th element of the returned
* array (after checking its length of course!).
*/
SmallVideo.prototype.selectVideoElement = function () {
return $(RTCUIHelper.findVideoElement($('#' + this.videoSpanId)[0]));
};
@@ -376,7 +391,7 @@ SmallVideo.prototype.updateView = function () {
let video = this.selectVideoElement();
let avatar = $(`#avatar_${this.id}`);
let avatar = $('#' + this.videoSpanId + ' .userAvatar');
var isCurrentlyOnLarge = this.VideoLayout.isCurrentlyOnLarge(this.id);
@@ -404,7 +419,7 @@ SmallVideo.prototype.updateView = function () {
SmallVideo.prototype.avatarChanged = function (avatarUrl) {
var thumbnail = $('#' + this.videoSpanId);
var avatar = $('#avatar_' + this.id);
var avatar = $('#' + this.videoSpanId + ' .userAvatar');
this.hasAvatar = true;
// set the avatar in the thumbnail
@@ -413,7 +428,6 @@ SmallVideo.prototype.avatarChanged = function (avatarUrl) {
} else {
if (thumbnail && thumbnail.length > 0) {
avatar = document.createElement('img');
avatar.id = 'avatar_' + this.id;
avatar.className = 'userAvatar';
avatar.src = avatarUrl;
thumbnail.append(avatar);
@@ -487,4 +501,37 @@ SmallVideo.prototype.getIndicatorSpan = function(id) {
return indicatorSpan;
};
/**
* Adds a listener for onresize events for this video, which will monitor for
* resolution changes, will calculate the delay since the moment the listened
* is added, and will fire a RESOLUTION_CHANGED event.
*/
SmallVideo.prototype.waitForResolutionChange = function() {
let self = this;
let beforeChange = window.performance.now();
let videos = this.selectVideoElement();
if (!videos || !videos.length || videos.length <= 0)
return;
let video = videos[0];
let oldWidth = video.videoWidth;
let oldHeight = video.videoHeight;
video.onresize = (event) => {
if (video.videoWidth != oldWidth || video.videoHeight != oldHeight) {
// Only run once.
video.onresize = null;
let delay = window.performance.now() - beforeChange;
let emitter = self.VideoLayout.getEventEmitter();
if (emitter) {
emitter.emit(
UIEvents.RESOLUTION_CHANGED,
self.getId(),
oldWidth + "x" + oldHeight,
video.videoWidth + "x" + video.videoHeight,
delay);
}
}
};
};
export default SmallVideo;

View File

@@ -162,7 +162,7 @@ var VideoLayout = {
localVideoThumbnail.setDisplayName();
localVideoThumbnail.createConnectionIndicator();
let localId = APP.conference.localId;
let localId = APP.conference.getMyUserId();
this.onVideoTypeChanged(localId, stream.videoType);
let {thumbWidth, thumbHeight} = this.resizeThumbnails(false, true);
@@ -186,7 +186,7 @@ var VideoLayout = {
*/
mucJoined () {
if (largeVideo && !largeVideo.id) {
this.updateLargeVideo(APP.conference.localId, true);
this.updateLargeVideo(APP.conference.getMyUserId(), true);
}
},
@@ -290,7 +290,7 @@ var VideoLayout = {
// Go with local video
console.info("Fallback to local video...");
let id = APP.conference.localId;
let id = APP.conference.getMyUserId();
console.info("electLastVisibleVideo: " + id);
return id;
@@ -457,6 +457,8 @@ var VideoLayout = {
let isModerator = APP.conference.isModerator;
if (isModerator) {
localVideoThumbnail.createModeratorIndicatorElement();
} else {
localVideoThumbnail.removeModeratorIndicatorElement();
}
APP.conference.listMembers().forEach(function (member) {
@@ -775,7 +777,7 @@ var VideoLayout = {
updateLocalConnectionStats (percent, object) {
let resolutions = object.resolution;
object.resolution = resolutions[APP.conference.localId];
object.resolution = resolutions[APP.conference.getMyUserId()];
localVideoThumbnail.updateStatsIndicator(percent, object);
Object.keys(resolutions).forEach(function (id) {
@@ -1010,11 +1012,16 @@ var VideoLayout = {
if (id !== currentId && videoType === VIDEO_CONTAINER_TYPE) {
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
}
if (currentId) {
var oldSmallVideo = this.getSmallVideo(currentId);
}
let smallVideo = this.getSmallVideo(id);
let oldSmallVideo;
if (currentId) {
oldSmallVideo = this.getSmallVideo(currentId);
}
smallVideo.waitForResolutionChange();
if (oldSmallVideo)
oldSmallVideo.waitForResolutionChange();
largeVideo.updateLargeVideo(
id,
@@ -1118,7 +1125,9 @@ var VideoLayout = {
setLocalFlipX: function (val) {
this.localFlipX = val;
}
},
getEventEmitter: () => {return eventEmitter;}
};
export default VideoLayout;

View File

@@ -23,9 +23,9 @@
"i18next-client": "1.7.7",
"jquery": "~2.1.1",
"jQuery-Impromptu": "git+https://github.com/trentrichardson/jQuery-Impromptu.git#v6.0.0",
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
"lib-jitsi-meet": "git+https://github.com/jitsi/lib-jitsi-meet.git",
"jquery-contextmenu": "*",
"jquery-ui": "^1.10.5",
"jquery-ui": "1.10.5",
"jssha": "1.5.0",
"retry": "0.6.1",
"strophe": "^1.2.2",

View File

@@ -80,5 +80,8 @@ export default {
/**
* Notifies that flipX property of the local video is changed.
*/
LOCAL_FLIPX_CHANGED: "UI.local_flipx_changed"
LOCAL_FLIPX_CHANGED: "UI.local_flipx_changed",
// An event which indicates that the resolution of a remote video has
// changed.
RESOLUTION_CHANGED: "UI.resolution_changed"
};