Adds a feedback button and dialog.

This commit is contained in:
yanas 2015-11-05 11:27:26 -06:00
parent 2117aefacc
commit 73b1d3c7c2
10 changed files with 345 additions and 35 deletions

View File

@ -3,7 +3,7 @@ BROWSERIFY = ./node_modules/.bin/browserify
UGLIFYJS = ./node_modules/.bin/uglifyjs UGLIFYJS = ./node_modules/.bin/uglifyjs
EXORCIST = ./node_modules/.bin/exorcist EXORCIST = ./node_modules/.bin/exorcist
CLEANCSS = ./node_modules/.bin/cleancss CLEANCSS = ./node_modules/.bin/cleancss
CSS_FILES = font.css toastr.css main.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 CSS_FILES = font.css toastr.css main.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
DEPLOY_DIR = libs DEPLOY_DIR = libs
BROWSERIFY_FLAGS = -d BROWSERIFY_FLAGS = -d
OUTPUT_DIR = . OUTPUT_DIR = .

48
css/feedback.css Normal file
View File

@ -0,0 +1,48 @@
/**
* The feedback window inner div css.
*/
.feedback {
width: 450px;
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
font-size: 22px;
}
/**
* Style of the thank you text inside the feedback window.
*/
.feedbackTitle {
font-size: 22px;
color: #087dba;
}
/**
* Stars div css.
*/
#stars {
font-size: 30px;
}
/**
* Star css.
*/
#stars>a {
padding-right: 4px;
}
/**
* Mouse over a star.
*/
.starHover {
color: #087dba;
}
/**
* Detailed feedback section text area style.
*/
.feedbackDetails textarea {
resize: vertical;
min-height: 100px;
}

View File

@ -10,7 +10,7 @@ html, body{
font-family:'Helvetica Neue', Helvetica, sans-serif; font-family:'Helvetica Neue', Helvetica, sans-serif;
font-weight: 400; font-weight: 400;
background: #000000; background: #000000;
overflow-x: hidden; overflow: hidden;
} }
.right-panel { .right-panel {
@ -236,10 +236,34 @@ form {
bottom: 5; bottom: 5;
left: 5; left: 5;
overflow: visible; overflow: visible;
z-index: 100;
color: rgba(255,255,255,.50); color: rgba(255,255,255,.50);
} }
#feedbackButton {
position: absolute;
bottom: 60;
left: 60;
overflow: visible;
color: rgba(255,255,255,.50);
}
div.feedbackButton {
position: absolute;
background-color: rgba(0,0,0,.50);
border-radius: 50%;
width: 100px;
height: 100px;
bottom: -50px;
left: -50px;
z-index: 100;
overflow: hidden;
transition: all .2s ease-in-out;
}
div.feedbackButton:hover {
transform: scale(1.3);
}
#bottomToolbar { #bottomToolbar {
display:block; display:block;
position: absolute; position: absolute;

View File

@ -14,7 +14,7 @@
<script src="https://api.callstats.io/static/callstats.min.js"></script> <script src="https://api.callstats.io/static/callstats.min.js"></script>
<script src="config.js?v=15"></script><!-- adapt to your needs, i.e. set hosts and bosh path --> <script src="config.js?v=15"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
<script src="interface_config.js?v=6"></script> <script src="interface_config.js?v=6"></script>
<script src="libs/app.bundle.min.js?v=138"></script> <script src="libs/app.bundle.min.js?v=139"></script>
<script src="analytics.js?v=1"></script><!-- google analytics plugin --> <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<!-- <!--
Link used for inline installation of chrome desktop streaming extension, Link used for inline installation of chrome desktop streaming extension,
@ -218,8 +218,11 @@
</label> </label>
</div> </div>
<button id="updateSettings" data-i18n="settings.update"></button> <button id="updateSettings" data-i18n="settings.update"></button>
</div>
<a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a> <a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
</div> </div>
<div class="feedbackButton">
<a id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]feedback"><i class="fa fa-heart"></i></a>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -3,6 +3,7 @@
"connectionsettings": "Connection Settings", "connectionsettings": "Connection Settings",
"poweredby": "powered by", "poweredby": "powered by",
"downloadlogs": "Download logs", "downloadlogs": "Download logs",
"feedback": "Give us your feedback",
"roomUrlDefaultMsg": "Your conference is currently being created...", "roomUrlDefaultMsg": "Your conference is currently being created...",
"participant": "Participant", "participant": "Participant",
"me": "me", "me": "me",
@ -197,7 +198,10 @@
"tokenAuthFailed": "Failed to authenticate with XMPP server: invalid token", "tokenAuthFailed": "Failed to authenticate with XMPP server: invalid token",
"displayNameRequired": "Please enter your display name:", "displayNameRequired": "Please enter your display name:",
"extensionRequired": "Extension required:", "extensionRequired": "Extension required:",
"firefoxExtensionPrompt": "You need to install a Firefox extension in order to use screen sharing. Please try again after you <a href='__url__'>get it from here</a>!" "firefoxExtensionPrompt": "You need to install a Firefox extension in order to use screen sharing. Please try again after you <a href='__url__'>get it from here</a>!",
"feedbackQuestion": "How was your call?",
"thankYou": "Thank you for using __appName__!",
"sorryFeedback": "We're sorry to hear that. Would you like to tell us more?"
}, },
"email": "email":
{ {

214
modules/UI/Feedback.js Normal file
View File

@ -0,0 +1,214 @@
/**
* Created by ystamcheva on 2/10/15.
*/
/* jshint -W101 */
var messageHandler = require("./util/MessageHandler");
var callStats = require("../statistics/CallStats");
var APP = require("../../app");
/**
* Constructs the html for the overall feedback window.
*
* @returns {string} the constructed html string
*/
var constructOverallFeedbackHtml = function() {
var feedbackQuestion = (Feedback.feedbackScore < 0)
? '<br/><br/>' + APP.translation
.translateString("dialog.feedbackQuestion")
: '';
var message = '<div class="feedback"><div>' +
'<div class="feedbackTitle">' +
APP.translation.translateString("dialog.thankYou",
{appName:interfaceConfig.APP_NAME}) +
'</div>' +
feedbackQuestion +
'</div><br/><br/>' +
'<div id="stars">' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'</div></div>';
return message;
};
/**
* Constructs the html for the detailed feedback window.
*
* @returns {string} the contructed html string
*/
var constructDetailedFeedbackHtml = function() {
// Construct the html, which will be served as a dialog message.
var message = '<div class="feedback">' +
'<div class="feedbackTitle">' +
APP.translation.translateString("dialog.sorryFeedback") +
'</div><br/><br/>' +
'<div class="feedbackDetails">' +
'<textarea id="feedbackTextArea" rows="10" cols="50" autofocus>' +
'</textarea>' +
'</div></div>';
return message;
};
/**
* The callback function corresponding to the openFeedbackWindow parameter.
*
* @type {function}
*/
var feedbackWindowCallback = null;
/**
* Defines all methods in connection to the Feedback window.
*
* @type {{feedbackScore: number, openFeedbackWindow: Function,
* toggleStars: Function, hoverStars: Function, unhoverStars: Function}}
*/
var Feedback = {
/**
* The feedback score. -1 indicates no score has been given for now.
*/
feedbackScore: -1,
/**
* Opens the feedback window.
*/
openFeedbackWindow: function (callback) {
feedbackWindowCallback = callback;
// Add all mouse and click listeners.
var onLoadFunction = function (event) {
$('#stars >a').each(function(index) {
// On star mouse over.
$(this).get(0).onmouseover = function(){
Feedback.hoverStars(index);
};
// On star mouse leave.
$(this).get(0).onmouseleave = function(){
Feedback.unhoverStars(index);
};
// On star click.
$(this).get(0).onclick = function(){
Feedback.toggleStars(index);
Feedback.feedbackScore = index+1;
// If the feedback is less than 3 stars we're going to
// ask the user for more information.
if (Feedback.feedbackScore > 3) {
callStats.sendFeedback(Feedback.feedbackScore, "");
if (feedbackWindowCallback)
feedbackWindowCallback();
else
APP.UI.messageHandler.closeDialog();
}
else {
feedbackDialog.goToState('detailed_feedback');
}
};
// Initialise stars to correspond to previously entered feedback.
if (Feedback.feedbackScore > 0
&& index < Feedback.feedbackScore) {
Feedback.hoverStars(index);
Feedback.toggleStars(index);
}
});
};
// Defines the different states of the feedback window.
var states = {
overall_feedback: {
html: constructOverallFeedbackHtml(),
persistent: true,
buttons: {},
closeText: '',
focus: "div[id='stars']",
position: {width: 500}
},
detailed_feedback: {
html: constructDetailedFeedbackHtml(),
buttons: {"Submit": true, "Cancel": false},
closeText: '',
focus: "textarea[id='feedbackTextArea']",
position: {width: 500},
submit: function(e,v,m,f) {
e.preventDefault();
if (v) {
var feedbackDetails
= document.getElementById("feedbackTextArea").value;
if (feedbackDetails && feedbackDetails.length > 0)
callStats.sendFeedback( Feedback.feedbackScore,
feedbackDetails);
if (feedbackWindowCallback)
feedbackWindowCallback();
else
APP.UI.messageHandler.closeDialog();
} else {
// User cancelled
if (feedbackWindowCallback)
feedbackWindowCallback();
else
APP.UI.messageHandler.closeDialog();
}
}
}
};
// Create the feedback dialog.
var feedbackDialog
= APP.UI.messageHandler.openDialogWithStates(
states,
{ persistent: true,
buttons: {},
closeText: '',
loaded: onLoadFunction,
position: {width: 500}}, null);
},
/**
* Toggles the appropriate css class for the given number of stars, to
* indicate that those stars have been clicked/selected.
*
* @param starCount the number of stars, for which to toggle the css class
*/
toggleStars: function (starCount)
{
$('#stars >a >i').each(function(index) {
if (index <= starCount) {
$(this).removeClass("fa-star-o");
}
else
$(this).addClass("fa-star-o");
});
},
/**
* Toggles the appropriate css class for the given number of stars, to
* indicate that those stars have been hovered.
*
* @param starCount the number of stars, for which to toggle the css class
*/
hoverStars: function (starCount)
{
$('#stars >a >i').each(function(index) {
if (index <= starCount)
$(this).addClass("starHover");
});
},
/**
* Toggles the appropriate css class for the given number of stars, to
* indicate that those stars have been un-hovered.
*
* @param starCount the number of stars, for which to toggle the css class
*/
unhoverStars: function (starCount)
{
$('#stars >a >i').each(function(index) {
if (index <= starCount && $(this).hasClass("fa-star-o"))
$(this).removeClass("starHover");
});
}
};
// Exports the Feedback class.
module.exports = Feedback;

View File

@ -33,6 +33,7 @@ var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var StatisticsEvents = require("../../service/statistics/Events"); var StatisticsEvents = require("../../service/statistics/Events");
var UIEvents = require("../../service/UI/UIEvents"); var UIEvents = require("../../service/UI/UIEvents");
var MemberEvents = require("../../service/members/Events"); var MemberEvents = require("../../service/members/Events");
var Feedback = require("./Feedback");
var eventEmitter = new EventEmitter(); var eventEmitter = new EventEmitter();
var roomNode = null; var roomNode = null;
@ -423,11 +424,15 @@ UI.start = function (init) {
$("#downloadlog").click(function (event) { $("#downloadlog").click(function (event) {
dump(event.target); dump(event.target);
}); });
$("#feedbackButton").click(function (event) {
Feedback.openFeedbackWindow();
});
} }
else else
{ {
$("#header").css("display", "none"); $("#header").css("display", "none");
$("#bottomToolbar").css("display", "none"); $("#bottomToolbar").css("display", "none");
$("#feedbackButton").css("display", "none");
$("#downloadlog").css("display", "none"); $("#downloadlog").css("display", "none");
$("#remoteVideos").css("padding", "0px 0px 18px 0px"); $("#remoteVideos").css("padding", "0px 0px 18px 0px");
$("#remoteVideos").css("right", "0px"); $("#remoteVideos").css("right", "0px");

View File

@ -11,6 +11,7 @@ var UIUtil = require("../util/UIUtil");
var AuthenticationEvents var AuthenticationEvents
= require("../../../service/authentication/AuthenticationEvents"); = require("../../../service/authentication/AuthenticationEvents");
var AnalyticsAdapter = require("../../statistics/AnalyticsAdapter"); var AnalyticsAdapter = require("../../statistics/AnalyticsAdapter");
var Feedback = require("../Feedback");
var roomUrl = null; var roomUrl = null;
var sharedKey = ''; var sharedKey = '';
@ -135,41 +136,32 @@ var defaultToolbarButtons = {
'hangup': '#toolbar_button_hangup' 'hangup': '#toolbar_button_hangup'
}; };
/**
* Hangs up this call.
*/
function hangup() { function hangup() {
var conferenceDispose = function () {
APP.xmpp.disposeConference(); APP.xmpp.disposeConference();
if(config.enableWelcomePage) {
if (config.enableWelcomePage) {
setTimeout(function() { setTimeout(function() {
window.localStorage.welcomePageDisabled = false; window.localStorage.welcomePageDisabled = false;
window.location.pathname = "/"; window.location.pathname = "/";
}, 10000); }, 3000);
}
} }
var title = APP.translation.generateTranslationHTML( if (Feedback.feedbackScore > 0) {
"dialog.sessTerminated"); Feedback.openFeedbackWindow();
var msg = APP.translation.generateTranslationHTML( conferenceDispose();
"dialog.hungUp");
var button = APP.translation.generateTranslationHTML(
"dialog.joinAgain");
var buttons = [];
buttons.push({title: button, value: true});
UI.messageHandler.openDialog(
title,
msg,
true,
buttons,
function(event, value, message, formVals) {
window.location.reload();
return false;
} }
); else
Feedback.openFeedbackWindow(conferenceDispose);
} }
/** /**
* Starts or stops the recording for the conference. * Starts or stops the recording for the conference.
*/ */
function toggleRecording(predefinedToken) { function toggleRecording(predefinedToken) {
APP.xmpp.toggleRecording(function (callback) { APP.xmpp.toggleRecording(function (callback) {
if (predefinedToken) { if (predefinedToken) {

View File

@ -84,7 +84,8 @@ var messageHandler = (function(my) {
}; };
/** /**
* Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel. * Shows a message to the user with two buttons: first is given as a
* parameter and the second is Cancel.
* *
* @param titleString the title of the message * @param titleString the title of the message
* @param msgString the text of the message * @param msgString the text of the message

View File

@ -77,7 +77,26 @@ var CallStats = {
} }
callStats.sendFabricEvent(this.peerconnection, callStats.sendFabricEvent(this.peerconnection,
callStats.fabricEvent.fabricSetupFailed, this.confID); callStats.fabricEvent.fabricSetupFailed, this.confID);
},
/**
* Sends the given feedback through CallStats.
*
* @param overallFeedback an integer between 1 and 5 indicating the
* user feedback
* @param detailedFeedback detailed feedback from the user. Not yet used
*/
sendFeedback: function(overallFeedback, detailedFeedback) {
if(!callStats) {
return;
} }
var feedbackString = '{"userID":"' + this.userID + '"' +
', "overall":' + overallFeedback +
', "comment": "' + detailedFeedback + '"}';
var feedbackJSON = JSON.parse(feedbackString);
callStats.sendUserFeedback(
this.confID, feedbackJSON);
}
}; };
module.exports = CallStats; module.exports = CallStats;