mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1065203 - Add some sound notifications for Loop's standalone link-clicker ui. r=nperriault
This commit is contained in:
parent
34d549e888
commit
9a1c8be5b2
@ -97,7 +97,58 @@ loop.shared.mixins = (function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio mixin. Allows playing a single audio file and ensuring it
|
||||
* is stopped when the component is unmounted.
|
||||
*/
|
||||
var AudioMixin = {
|
||||
audio: null,
|
||||
|
||||
_isLoopDesktop: function() {
|
||||
return typeof rootObject.navigator.mozLoop === "object";
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts playing an audio file, stopping any audio that is already in progress.
|
||||
*
|
||||
* @param {String} filename The filename to play (excluding the extension).
|
||||
*/
|
||||
play: function(filename, options) {
|
||||
if (this._isLoopDesktop()) {
|
||||
// XXX: We need navigator.mozLoop.playSound(name), see Bug 1089585.
|
||||
return;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
options.loop = options.loop || false;
|
||||
|
||||
this._ensureAudioStopped();
|
||||
this.audio = new Audio('shared/sounds/' + filename + ".ogg");
|
||||
this.audio.loop = options.loop;
|
||||
this.audio.play();
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures audio is stopped playing, and removes the object from memory.
|
||||
*/
|
||||
_ensureAudioStopped: function() {
|
||||
if (this.audio) {
|
||||
this.audio.pause();
|
||||
this.audio.removeAttribute("src");
|
||||
delete this.audio;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures audio is stopped when the component is unmounted.
|
||||
*/
|
||||
componentWillUnmount: function() {
|
||||
this._ensureAudioStopped();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
AudioMixin: AudioMixin,
|
||||
setRootObject: setRootObject,
|
||||
DropdownMenuMixin: DropdownMenuMixin,
|
||||
DocumentVisibilityMixin: DocumentVisibilityMixin
|
||||
|
@ -135,7 +135,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
* Conversation view.
|
||||
*/
|
||||
var ConversationView = React.createClass({displayName: 'ConversationView',
|
||||
mixins: [Backbone.Events],
|
||||
mixins: [Backbone.Events, sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
@ -183,7 +183,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
componentDidMount: function() {
|
||||
if (this.props.initiate) {
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this.startPublishing);
|
||||
this._onSessionConnected);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
this._streamCreated);
|
||||
this.listenTo(this.props.model, ["session:peer-hungup",
|
||||
@ -225,6 +225,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
this.props.model.endSession();
|
||||
},
|
||||
|
||||
_onSessionConnected: function(event) {
|
||||
this.startPublishing(event);
|
||||
this.play("connected");
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes and attaches each created stream to a DOM element.
|
||||
*
|
||||
|
@ -135,7 +135,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
* Conversation view.
|
||||
*/
|
||||
var ConversationView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
mixins: [Backbone.Events, sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
@ -183,7 +183,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
componentDidMount: function() {
|
||||
if (this.props.initiate) {
|
||||
this.listenTo(this.props.model, "session:connected",
|
||||
this.startPublishing);
|
||||
this._onSessionConnected);
|
||||
this.listenTo(this.props.model, "session:stream-created",
|
||||
this._streamCreated);
|
||||
this.listenTo(this.props.model, ["session:peer-hungup",
|
||||
@ -225,6 +225,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
this.props.model.endSession();
|
||||
},
|
||||
|
||||
_onSessionConnected: function(event) {
|
||||
this.startPublishing(event);
|
||||
this.play("connected");
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribes and attaches each created stream to a DOM element.
|
||||
*
|
||||
|
BIN
browser/components/loop/content/shared/sounds/connected.ogg
Normal file
BIN
browser/components/loop/content/shared/sounds/connected.ogg
Normal file
Binary file not shown.
BIN
browser/components/loop/content/shared/sounds/connecting.ogg
Normal file
BIN
browser/components/loop/content/shared/sounds/connecting.ogg
Normal file
Binary file not shown.
BIN
browser/components/loop/content/shared/sounds/ringing.ogg
Normal file
BIN
browser/components/loop/content/shared/sounds/ringing.ogg
Normal file
Binary file not shown.
BIN
browser/components/loop/content/shared/sounds/terminated.ogg
Normal file
BIN
browser/components/loop/content/shared/sounds/terminated.ogg
Normal file
Binary file not shown.
@ -262,9 +262,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
});
|
||||
|
||||
var PendingConversationView = React.createClass({displayName: 'PendingConversationView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callState: this.props.callState || "connecting"
|
||||
callState: "connecting"
|
||||
};
|
||||
},
|
||||
|
||||
@ -274,11 +277,13 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("connecting", {loop: true});
|
||||
this.props.websocket.listenTo(this.props.websocket, "progress:alerting",
|
||||
this._handleRingingProgress);
|
||||
},
|
||||
|
||||
_handleRingingProgress: function() {
|
||||
this.play("ringing", {loop: true});
|
||||
this.setState({callState: "ringing"});
|
||||
},
|
||||
|
||||
@ -518,6 +523,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* Ended conversation view.
|
||||
*/
|
||||
var EndedConversationView = React.createClass({displayName: 'EndedConversationView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
@ -526,6 +533,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("terminated");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
{clientShortname: mozL10n.get("clientShortname2"),
|
||||
|
@ -262,9 +262,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
});
|
||||
|
||||
var PendingConversationView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callState: this.props.callState || "connecting"
|
||||
callState: "connecting"
|
||||
};
|
||||
},
|
||||
|
||||
@ -274,11 +277,13 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("connecting", {loop: true});
|
||||
this.props.websocket.listenTo(this.props.websocket, "progress:alerting",
|
||||
this._handleRingingProgress);
|
||||
},
|
||||
|
||||
_handleRingingProgress: function() {
|
||||
this.play("ringing", {loop: true});
|
||||
this.setState({callState: "ringing"});
|
||||
},
|
||||
|
||||
@ -518,6 +523,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* Ended conversation view.
|
||||
*/
|
||||
var EndedConversationView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
@ -526,6 +533,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("terminated");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
{clientShortname: mozL10n.get("clientShortname2"),
|
||||
|
@ -161,13 +161,20 @@ describe("loop.shared.views", function() {
|
||||
});
|
||||
|
||||
describe("ConversationView", function() {
|
||||
var fakeSDK, fakeSessionData, fakeSession, fakePublisher, model;
|
||||
var fakeSDK, fakeSessionData, fakeSession, fakePublisher, model, fakeAudio;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
return TestUtils.renderIntoDocument(sharedViews.ConversationView(props));
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
|
||||
fakeSessionData = {
|
||||
sessionId: "sessionId",
|
||||
sessionToken: "sessionToken",
|
||||
@ -350,46 +357,69 @@ describe("loop.shared.views", function() {
|
||||
});
|
||||
|
||||
describe("Model events", function() {
|
||||
it("should start streaming on session:connected", function() {
|
||||
model.trigger("session:connected");
|
||||
|
||||
sinon.assert.calledOnce(fakeSDK.initPublisher);
|
||||
});
|
||||
describe("for standalone", function() {
|
||||
|
||||
it("should publish remote stream on session:stream-created",
|
||||
function() {
|
||||
var s1 = {connection: {connectionId: 42}};
|
||||
|
||||
model.trigger("session:stream-created", {stream: s1});
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.subscribe);
|
||||
sinon.assert.calledWith(fakeSession.subscribe, s1);
|
||||
beforeEach(function() {
|
||||
// In standalone, navigator.mozLoop does not exists
|
||||
if (navigator.hasOwnProperty("mozLoop"))
|
||||
sandbox.stub(navigator, "mozLoop", undefined);
|
||||
});
|
||||
|
||||
it("should unpublish local stream on session:ended", function() {
|
||||
comp.startPublishing();
|
||||
it("should play a connected sound, once, on session:connected",
|
||||
function() {
|
||||
model.trigger("session:connected");
|
||||
|
||||
model.trigger("session:ended");
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.unpublish);
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(
|
||||
window.Audio, "shared/sounds/connected.ogg");
|
||||
expect(fakeAudio.loop).to.not.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should unpublish local stream on session:peer-hungup", function() {
|
||||
comp.startPublishing();
|
||||
describe("for both (standalone and desktop)", function() {
|
||||
it("should start streaming on session:connected", function() {
|
||||
model.trigger("session:connected");
|
||||
|
||||
model.trigger("session:peer-hungup");
|
||||
sinon.assert.calledOnce(fakeSDK.initPublisher);
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.unpublish);
|
||||
});
|
||||
it("should publish remote stream on session:stream-created",
|
||||
function() {
|
||||
var s1 = {connection: {connectionId: 42}};
|
||||
|
||||
it("should unpublish local stream on session:network-disconnected",
|
||||
function() {
|
||||
model.trigger("session:stream-created", {stream: s1});
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.subscribe);
|
||||
sinon.assert.calledWith(fakeSession.subscribe, s1);
|
||||
});
|
||||
|
||||
it("should unpublish local stream on session:ended", function() {
|
||||
comp.startPublishing();
|
||||
|
||||
model.trigger("session:network-disconnected");
|
||||
model.trigger("session:ended");
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.unpublish);
|
||||
});
|
||||
|
||||
it("should unpublish local stream on session:peer-hungup", function() {
|
||||
comp.startPublishing();
|
||||
|
||||
model.trigger("session:peer-hungup");
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.unpublish);
|
||||
});
|
||||
|
||||
it("should unpublish local stream on session:network-disconnected",
|
||||
function() {
|
||||
comp.startPublishing();
|
||||
|
||||
model.trigger("session:network-disconnected");
|
||||
|
||||
sinon.assert.calledOnce(fakeSession.unpublish);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Publisher events", function() {
|
||||
|
@ -575,7 +575,7 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
describe("PendingConversationView", function() {
|
||||
var view, websocket;
|
||||
var view, websocket, fakeAudio;
|
||||
|
||||
beforeEach(function() {
|
||||
websocket = new loop.CallConnectionWebSocket({
|
||||
@ -585,6 +585,12 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
sinon.stub(websocket, "cancel");
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.PendingConversationView({
|
||||
@ -593,6 +599,16 @@ describe("loop.webapp", function() {
|
||||
);
|
||||
});
|
||||
|
||||
describe("#componentDidMount", function() {
|
||||
|
||||
it("should play a looped connecting sound", function() {
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/connecting.ogg");
|
||||
expect(fakeAudio.loop).to.equal(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#_cancelOutgoingCall", function() {
|
||||
it("should inform the websocket to cancel the setup", function() {
|
||||
var button = view.getDOMNode().querySelector(".btn-cancel");
|
||||
@ -609,6 +625,13 @@ describe("loop.webapp", function() {
|
||||
|
||||
expect(view.state.callState).to.be.equal("ringing");
|
||||
});
|
||||
|
||||
it("should play a looped ringing sound", function() {
|
||||
websocket.trigger("progress:alerting");
|
||||
|
||||
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/ringing.ogg");
|
||||
expect(fakeAudio.loop).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -843,9 +866,16 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
describe("EndedConversationView", function() {
|
||||
var view, conversation;
|
||||
var view, conversation, fakeAudio;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: {}
|
||||
});
|
||||
@ -866,6 +896,17 @@ describe("loop.webapp", function() {
|
||||
it("should render a FeedbackView", function() {
|
||||
TestUtils.findRenderedComponentWithType(view, sharedViews.FeedbackView);
|
||||
});
|
||||
|
||||
describe("#componentDidMount", function() {
|
||||
|
||||
it("should play a terminating sound, once", function() {
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/terminated.ogg");
|
||||
expect(fakeAudio.loop).to.not.equal(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("PromoteFirefoxView", function() {
|
||||
|
Loading…
Reference in New Issue
Block a user