Fixes issues with invalid avatars and problems with switching between videos.

This commit is contained in:
paweldomas 2015-07-20 19:32:04 +02:00
parent a66459e206
commit c3548eb866
11 changed files with 19607 additions and 19536 deletions

View File

@ -22,7 +22,7 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
<script src="interface_config.js?v=5"></script>
<script src="libs/app.bundle.js?v=106"></script>
<script src="libs/app.bundle.js?v=107"></script>
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<link rel="stylesheet" href="css/font.css?v=7"/>
<link rel="stylesheet" href="css/toastr.css?v=1">

File diff suppressed because it is too large Load Diff

View File

@ -449,8 +449,12 @@ function onMucJoined(jid, info) {
$("#localNick").html(Strophe.getResourceFromJid(jid) + " (" + meHTML + ")");
var settings = Settings.getSettings();
// Make sure we configure our avatar id, before creating avatar for us
Avatar.setUserAvatar(jid, settings.email || settings.uid);
// Add myself to the contact list.
ContactList.addContact(jid, settings.email || settings.uid);
ContactList.addContact(jid);
// Once we've joined the muc show the toolbar
ToolbarToggler.showToolbar();
@ -572,8 +576,12 @@ function onMucMemberJoined(jid, id, displayName) {
if(!config.startAudioMuted ||
config.startAudioMuted > APP.members.size())
UIUtil.playSoundNotification('userJoined');
// Configure avatar
Avatar.setUserAvatar(jid, id);
// Add Peer's container
VideoLayout.ensurePeerContainerExists(jid,id);
VideoLayout.ensurePeerContainerExists(jid);
}
function onMucPresenceStatus(jid, info) {

View File

@ -17,15 +17,43 @@ var Avatar = {
}
users[jid] = id;
}
var thumbUrl = this.getGravatarUrl(users[jid] || jid, 100);
var contactListUrl = this.getGravatarUrl(users[jid] || jid);
var thumbUrl = this.getThumbUrl(jid);
var contactListUrl = this.getContactListUrl(jid);
var resourceJid = Strophe.getResourceFromJid(jid);
APP.UI.userAvatarChanged(resourceJid, thumbUrl, contactListUrl);
},
getGravatarUrl: function (id, size) {
if(id === APP.xmpp.myJid() || !id) {
id = Settings.getSettings().uid;
/**
* Returns image URL for the avatar to be displayed on large video area
* where current active speaker is presented.
* @param jid full MUC jid of the user for whom we want to obtain avatar URL
*/
getActiveSpeakerUrl: function (jid) {
return this.getGravatarUrl(jid, 100);
},
/**
* Returns image URL for the avatar to be displayed on small video thumbnail
* @param jid full MUC jid of the user for whom we want to obtain avatar URL
*/
getThumbUrl: function (jid) {
return this.getGravatarUrl(jid, 100);
},
/**
* Returns the URL for the avatar to be displayed as contactlist item
* @param jid full MUC jid of the user for whom we want to obtain avatar URL
*/
getContactListUrl: function (jid) {
return this.getGravatarUrl(jid, 30);
},
getGravatarUrl: function (jid, size) {
if (!jid) {
console.error("Get gravatar - jid is undefined");
return null;
}
var id = users[jid];
if (!id) {
console.warn("No avatar stored yet for " + jid);
return null;
}
return 'https://www.gravatar.com/avatar/' +
MD5.hexdigest(id.trim().toLowerCase()) +

View File

@ -1,4 +1,6 @@
var Avatar = require('../../avatar/Avatar');
var numberOfContacts = 0;
var notificationInterval;
@ -27,10 +29,10 @@ function updateNumberOfParticipants(delta) {
*
* @return the newly created avatar element
*/
function createAvatar(id) {
function createAvatar(jid) {
var avatar = document.createElement('img');
avatar.className = "icon-avatar avatar";
avatar.src = "https://www.gravatar.com/avatar/" + id + "?d=wavatar&size=30";
avatar.src = Avatar.getContactListUrl(jid);
return avatar;
}
@ -82,24 +84,22 @@ var ContactList = {
* Adds a contact for the given peerJid if such doesn't yet exist.
*
* @param peerJid the peerJid corresponding to the contact
* @param id the user's email or userId used to get the user's avatar
*/
ensureAddContact: function (peerJid, id) {
ensureAddContact: function (peerJid) {
var resourceJid = Strophe.getResourceFromJid(peerJid);
var contact = $('#contacts>li[id="' + resourceJid + '"]');
if (!contact || contact.length <= 0)
ContactList.addContact(peerJid, id);
ContactList.addContact(peerJid);
},
/**
* Adds a contact for the given peer jid.
*
* @param peerJid the jid of the contact to add
* @param id the email or userId of the user
*/
addContact: function (peerJid, id) {
addContact: function (peerJid) {
var resourceJid = Strophe.getResourceFromJid(peerJid);
var contactlist = $('#contacts');
@ -113,7 +113,7 @@ var ContactList = {
}
};
newContact.appendChild(createAvatar(id));
newContact.appendChild(createAvatar(peerJid));
newContact.appendChild(createDisplayNameParagraph("participant"));
if (resourceJid === APP.xmpp.myResource()) {

View File

@ -155,7 +155,9 @@ ConnectionIndicator.prototype.generateText = function () {
if(this.videoContainer.videoSpanId == "localVideoContainer") {
result += "<div class=\"jitsipopover_showmore\" " +
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
this.jid + "')\" data-i18n='connectionindicator." +
// FIXME: we do not know local jid when this text is generated
//this.jid + "')\" data-i18n='connectionindicator." +
"local')\" data-i18n='connectionindicator." +
(this.showMoreValue ? "less" : "more") + "'>" +
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
"</div><br />";

View File

@ -185,12 +185,12 @@ function getCameraVideoSize(videoWidth,
function updateActiveSpeakerAvatarSrc() {
var avatar = $("#activeSpeakerAvatar")[0];
var jid = currentSmallVideo.peerJid;
var url = Avatar.getGravatarUrl(jid);
var url = Avatar.getActiveSpeakerUrl(jid);
if (avatar.src === url)
return;
var isMuted = null;
if (!currentSmallVideo.isLocal &&
!LargeVideo.VideoLayout.isInLastN(currentSmallVideo.resourceJid)) {
!LargeVideo.VideoLayout.isInLastN(currentSmallVideo.getResourceJid())) {
isMuted = true;
}
else
@ -280,6 +280,13 @@ var LargeVideo = {
isLargeVideoVisible: function() {
return $('#largeVideo').is(':visible');
},
/**
* Returns <tt>true</tt> if the user is currently displayed on large video.
*/
isCurrentlyOnLarge: function (resourceJid) {
return currentSmallVideo && resourceJid &&
currentSmallVideo.getResourceJid() === resourceJid;
},
/**
* Updates the large video with the given new video source.
*/
@ -287,8 +294,7 @@ var LargeVideo = {
var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
if ((currentSmallVideo && currentSmallVideo.resourceJid !== resourceJid)
|| forceUpdate) {
if (!LargeVideo.isCurrentlyOnLarge(resourceJid) || forceUpdate) {
$('#activeSpeaker').css('visibility', 'hidden');
if(currentSmallVideo) {
@ -337,7 +343,8 @@ var LargeVideo = {
}
},
onVideoTypeChanged: function (jid) {
if(jid && currentSmallVideo && jid === currentSmallVideo.peerJid)
var resourceJid = Strophe.getResourceFromJid(jid);
if (LargeVideo.isCurrentlyOnLarge(resourceJid))
{
var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
getVideoSize = isDesktop
@ -438,9 +445,7 @@ var LargeVideo = {
this.position(null, null, size[0], size[1], true);
},
getResourceJid: function () {
if(!currentSmallVideo)
return null;
return currentSmallVideo.resourceJid;
return currentSmallVideo ? currentSmallVideo.getResourceJid() : null;
},
updateAvatar: function (resourceJid) {
if (resourceJid === this.getResourceJid()) {

View File

@ -13,7 +13,6 @@ function LocalVideo(VideoLayout)
this.flipX = true;
this.isLocal = true;
this.peerJid = null;
this.resourceJid = null;
}
LocalVideo.prototype = Object.create(SmallVideo.prototype);
@ -184,8 +183,8 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
self.showDisplayName(true);
},
function() {
if (!LargeVideo.isLargeVideoVisible()
|| APP.xmpp.myResource() !== LargeVideo.getResourceJid())
if (!LargeVideo.isLargeVideoVisible() ||
!LargeVideo.isCurrentlyOnLarge(self.getResourceJid()))
self.showDisplayName(false);
}
);
@ -231,11 +230,18 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
localVideoContainer.removeChild(localVideo);
self.VideoLayout.updateRemovedVideo(APP.xmpp.myResource());
};
}
};
LocalVideo.prototype.joined = function (jid) {
this.peerJid = jid;
this.resourceJid = Strophe.getResourceFromJid(jid);
};
LocalVideo.prototype.getResourceJid = function () {
var myResource = APP.xmpp.myResource();
if (!myResource) {
console.error("Requested local resource before we're in the MUC");
}
return myResource;
};
module.exports = LocalVideo;

View File

@ -57,7 +57,7 @@ RemoteVideo.prototype.addRemoteVideoMenu = function () {
var popupmenuElement = document.createElement('ul');
popupmenuElement.className = 'popupmenu';
popupmenuElement.id
= 'remote_popupmenu_' + this.resourceJid;
= 'remote_popupmenu_' + this.getResourceJid();
spanElement.appendChild(popupmenuElement);
var muteMenuItem = document.createElement('li');
@ -162,7 +162,7 @@ RemoteVideo.prototype.removeRemoteStreamElement = function (stream, isVideo, id)
}
if (isVideo)
this.VideoLayout.updateRemovedVideo(this.resourceJid);
this.VideoLayout.updateRemovedVideo(this.getResourceJid());
};
RemoteVideo.prototype.waitForPlayback = function (stream) {
@ -173,7 +173,8 @@ RemoteVideo.prototype.waitForPlayback = function (stream) {
}
var self = this;
var sel = this.VideoLayout.getPeerVideoSel(this.resourceJid);
var resourceJid = this.getResourceJid();
var sel = this.VideoLayout.getPeerVideoSel(resourceJid);
// Register 'onplaying' listener to trigger 'videoactive' on VideoLayout
// when video playback starts
@ -183,9 +184,9 @@ RemoteVideo.prototype.waitForPlayback = function (stream) {
APP.RTC.attachMediaStream(sel, stream);
}
if (RTCBrowserType.isTemasysPluginUsed()) {
sel = $('#' + newElementId);
sel = self.VideoLayout.getPeerVideoSel(resourceJid);
}
self.VideoLayout.videoactive(sel, self.resourceJid);
self.VideoLayout.videoactive(sel, resourceJid);
sel[0].onplaying = null;
if (RTCBrowserType.isTemasysPluginUsed()) {
// 'currentTime' is used to check if the video has started
@ -229,16 +230,9 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
// Add click handler.
var onClickHandler = function (event) {
/*
* FIXME It turns out that videoThumb may not exist (if there is
* no actual video).
*/
var videoThumb = $('#' + self.videoSpanId + '>' + videoElem).get(0);
if (videoThumb) {
self.VideoLayout.handleVideoThumbClicked(
false,
self.resourceJid);
}
self.VideoLayout.handleVideoThumbClicked(false, self.getResourceJid());
// On IE we need to populate this handler on video <object>
// and it does not give event instance as an argument,
// so we check here for methods.
@ -261,16 +255,10 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
self.showDisplayName(true);
},
function() {
var videoSrc = null;
var videoSelector = $('#' + self.videoSpanId + '>' + videoElem);
if (videoSelector && videoSelector.length > 0) {
videoSrc = APP.RTC.getVideoSrc(videoSelector.get(0));
}
// If the video has been "pinned" by the user we want to
// keep the display name on place.
if (!LargeVideo.isLargeVideoVisible()
|| videoSrc !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
if (!LargeVideo.isLargeVideoVisible() ||
!LargeVideo.isCurrentlyOnLarge(self.getResourceJid()))
self.showDisplayName(false);
}
);
@ -330,9 +318,7 @@ RemoteVideo.prototype.hideConnectionIndicator = function () {
*/
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
var muteMenuItem
= $('#remote_popupmenu_'
+ this.resourceJid
+ '>li>a.mutelink');
= $('#remote_popupmenu_' + this.getResourceJid() + '>li>a.mutelink');
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
@ -348,7 +334,7 @@ RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
muteLink.className = 'mutelink';
}
}
}
};
/**
* Sets the display name for the given video span id.
@ -407,7 +393,14 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() {
if (menuSpan.length) {
menuSpan.remove();
}
};
RemoteVideo.prototype.getResourceJid = function () {
if (!this.resourceJid) {
console.error("Undefined resource jid");
}
return this.resourceJid;
};
RemoteVideo.createContainer = function (spanId) {
var container = document.createElement('span');

View File

@ -1,3 +1,4 @@
var Avatar = require("../avatar/Avatar");
var UIUtil = require("../util/UIUtil");
var LargeVideo = require("./LargeVideo");
var RTCBrowserType = require("../../RTC/RTCBrowserType");
@ -192,14 +193,15 @@ SmallVideo.prototype.showVideoIndicator = function(isMuted) {
SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
{
var displayName = this.resourceJid;
var resourceJid = this.getResourceJid();
var displayName = resourceJid;
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
if (nameSpan.length > 0)
displayName = nameSpan.html();
console.log("UI enable dominant speaker",
displayName,
this.resourceJid,
resourceJid,
isEnable);
@ -207,9 +209,6 @@ SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
return;
}
var video = $('#' + this.videoSpanId + '>' + APP.RTC.getVideoElementName());
if (video && video.length > 0) {
if (isEnable) {
this.showDisplayName(LargeVideo.isLargeVideoOnTop());
@ -222,7 +221,6 @@ SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
if (this.container.classList.contains("dominantspeaker"))
this.container.classList.remove("dominantspeaker");
}
}
this.showAvatar();
};
@ -305,16 +303,24 @@ SmallVideo.prototype.hasVideo = function () {
* video because there is no dominant speaker and no focused speaker
*/
SmallVideo.prototype.showAvatar = function (show) {
if (!this.hasAvatar)
if (!this.hasAvatar) {
if (this.peerJid) {
// Init avatar
this.avatarChanged(Avatar.getThumbUrl(this.peerJid));
} else {
console.error("Unable to init avatar - no peerjid", this);
return;
}
}
var resourceJid = this.getResourceJid();
var videoElem = APP.RTC.getVideoElementName();
var video = $('#' + this.videoSpanId).find(videoElem);
var avatar = $('#avatar_' + this.resourceJid);
var avatar = $('#avatar_' + resourceJid);
if (show === undefined || show === null) {
if (!this.isLocal &&
!this.VideoLayout.isInLastN(this.resourceJid)) {
!this.VideoLayout.isInLastN(resourceJid)) {
show = true;
}
else
@ -324,7 +330,7 @@ SmallVideo.prototype.showAvatar = function (show) {
}
if (LargeVideo.showAvatar(this.resourceJid, show))
if (LargeVideo.showAvatar(resourceJid, show))
{
setVisibility(avatar, false);
setVisibility(video, false);
@ -339,7 +345,8 @@ SmallVideo.prototype.showAvatar = function (show) {
SmallVideo.prototype.avatarChanged = function (thumbUrl) {
var thumbnail = $('#' + this.videoSpanId);
var avatar = $('#avatar_' + this.resourceJid);
var resourceJid = this.getResourceJid();
var avatar = $('#avatar_' + resourceJid);
this.hasAvatar = true;
// set the avatar in the thumbnail
@ -348,7 +355,7 @@ SmallVideo.prototype.avatarChanged = function (thumbUrl) {
} else {
if (thumbnail && thumbnail.length > 0) {
avatar = document.createElement('img');
avatar.id = 'avatar_' + this.resourceJid;
avatar.id = 'avatar_' + resourceJid;
avatar.className = 'userAvatar';
avatar.src = thumbUrl;
thumbnail.append(avatar);

View File

@ -72,11 +72,10 @@ var VideoLayout = (function (my) {
localVideoThumbnail.changeVideo(stream, isMuted);
LargeVideo.updateLargeVideo(
APP.xmpp.myResource(),
/* force update only before conference starts */
!APP.xmpp.isConferenceInProgress());
/* force update if we're currently being displayed */
if (LargeVideo.isCurrentlyOnLarge(APP.xmpp.myResource())) {
LargeVideo.updateLargeVideo(APP.xmpp.myResource(), true);
}
};
my.mucJoined = function () {
@ -116,54 +115,40 @@ var VideoLayout = (function (my) {
*/
my.updateRemovedVideo = function(resourceJid) {
var videoElem = RTC.getVideoElementName();
if (resourceJid === LargeVideo.getResourceJid()) {
// this is currently displayed as large
var newResourceJid;
// We'll show user's avatar if he is the dominant speaker or if
// his video thumbnail is pinned
if (resourceJid === focusedVideoResourceJid ||
resourceJid === currentDominantSpeaker) {
newResourceJid = resourceJid;
} else {
// Otherwise select last visible video
newResourceJid = this.electLastVisibleVideo();
}
LargeVideo.updateLargeVideo(newResourceJid);
}
};
my.electLastVisibleVideo = function() {
// pick the last visible video in the row
// if nobody else is left, this picks the local video
var pick
= $('#remoteVideos>' +
'span[id!="mixedstream"]:visible:last>' + videoElem).get(0);
if (!pick) {
var jid;
var videoElem = RTC.getVideoElementName();
var pick = $('#remoteVideos>span[id!="mixedstream"]:visible:last>' + videoElem);
if (pick.length && APP.RTC.getVideoSrc(pick[0])) {
return VideoLayout.getPeerContainerResourceJid(pick[0].parentNode);
} else {
console.info("Last visible video no longer exists");
pick = $('#remoteVideos>' +
'span[id!="mixedstream"]>' + videoElem).get(0);
if (!pick || !APP.RTC.getVideoSrc(pick)) {
pick = $('#remoteVideos>span[id!="mixedstream"]>' + videoElem);
if (pick.length && APP.RTC.getVideoSrc(pick[0])) {
return VideoLayout.getPeerContainerResourceJid(pick[0].parentNode);
} else {
// Try local video
console.info("Fallback to local video...");
pick = $('#remoteVideos>span>span>' + videoElem).get(0);
return APP.xmpp.myResource();
}
}
// mute if localvideo
if (pick) {
var container = pick.parentNode;
} else {
console.warn("Failed to elect large video");
container = $('#remoteVideos>span[id!="mixedstream"]:visible:last').get(0);
}
var jid = null;
if(container)
{
if(container.id == "localVideoWrapper")
{
jid = APP.xmpp.myResource();
}
else
{
jid = VideoLayout.getPeerContainerResourceJid(container);
}
}
else
return;
LargeVideo.updateLargeVideo(jid);
}
};
my.onRemoteStreamAdded = function (stream) {
@ -224,11 +209,6 @@ var VideoLayout = (function (my) {
}
}
if (LargeVideo.getResourceJid() === resourceJid &&
LargeVideo.isLargeVideoOnTop()) {
return;
}
// Triggers a "video.selected" event. The "false" parameter indicates
// this isn't a prezi.
$(document).trigger("video.selected", [false]);
@ -250,20 +230,18 @@ var VideoLayout = (function (my) {
* in the document and creates it eventually.
*
* @param peerJid peer Jid to check.
* @param userId user email or id for setting the avatar
*
* @return Returns <tt>true</tt> if the peer container exists,
* <tt>false</tt> - otherwise
*/
my.ensurePeerContainerExists = function(peerJid, userId) {
ContactList.ensureAddContact(peerJid, userId);
my.ensurePeerContainerExists = function(peerJid) {
ContactList.ensureAddContact(peerJid);
var resourceJid = Strophe.getResourceFromJid(peerJid);
if(!remoteVideos[resourceJid])
{
remoteVideos[resourceJid] = new RemoteVideo(peerJid, VideoLayout);
Avatar.setUserAvatar(peerJid, userId);
// In case this is not currently in the last n we don't show it.
if (localLastNCount
@ -282,6 +260,9 @@ var VideoLayout = (function (my) {
};
my.videoactive = function (videoelem, resourceJid) {
console.info(resourceJid + " video is now active");
videoelem.show();
VideoLayout.resizeThumbnails();
@ -291,6 +272,7 @@ var VideoLayout = (function (my) {
if ((!focusedVideoResourceJid &&
!currentDominantSpeaker &&
!require("../prezi/Prezi").isPresentationVisible()) ||
focusedVideoResourceJid === resourceJid ||
(resourceJid &&
currentDominantSpeaker === resourceJid)) {
LargeVideo.updateLargeVideo(resourceJid, true);
@ -469,7 +451,7 @@ var VideoLayout = (function (my) {
return;
}
if (jid == APP.xmpp.myJid()) {
if (jid === APP.xmpp.myJid()) {
$("#localVideoContainer").click();
return;
}
@ -824,13 +806,18 @@ var VideoLayout = (function (my) {
};
my.showMore = function (jid) {
if(APP.xmpp.myJid = jid)
if (jid === 'local')
{
localVideoThumbnail.connectionIndicator.showMore();
}
else
{
remoteVideos[Strophe.getResourceFromJid(jid)].connectionIndicator.showMore();
var remoteVideo = remoteVideos[Strophe.getResourceFromJid(jid)];
if (remoteVideo) {
remoteVideo.connectionIndicator.showMore();
} else {
console.info("Error - no remote video for jid: " + jid);
}
}
};
@ -880,6 +867,9 @@ var VideoLayout = (function (my) {
var smallVideo = VideoLayout.getSmallVideo(resourceJid);
if(smallVideo)
smallVideo.avatarChanged(thumbUrl);
else
console.warn(
"Missed avatar update - no small video yet for " + resourceJid);
LargeVideo.updateAvatar(resourceJid, thumbUrl);
};