From ea55d60018ecdbb9fe487a00478e0fdee9803f9e Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Tue, 18 Nov 2014 20:50:57 +0000 Subject: [PATCH] Bug 1088650 Add sounds for notifications when rooms are joined, left or if there are failures. r=nperriault --- .../components/loop/content/js/roomViews.js | 7 +- .../components/loop/content/js/roomViews.jsx | 7 +- .../loop/content/shared/js/mixins.js | 57 +++++++++++++ .../content/shared/sounds/room-joined-in.ogg | Bin 0 -> 9242 bytes .../loop/content/shared/sounds/room-left.ogg | Bin 0 -> 10028 bytes .../content/js/standaloneRoomViews.js | 5 +- .../content/js/standaloneRoomViews.jsx | 5 +- .../loop/test/shared/mixins_test.js | 78 ++++++++++++++++++ 8 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 browser/components/loop/content/shared/sounds/room-joined-in.ogg create mode 100644 browser/components/loop/content/shared/sounds/room-left.ogg diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index 3785a0e3651f..a4ce50d2bb4a 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -12,6 +12,7 @@ loop.roomViews = (function(mozL10n) { "use strict"; var sharedActions = loop.shared.actions; + var sharedMixins = loop.shared.mixins; var ROOM_STATES = loop.store.ROOM_STATES; var sharedViews = loop.shared.views; @@ -132,7 +133,11 @@ loop.roomViews = (function(mozL10n) { * Desktop room conversation view. */ var DesktopRoomConversationView = React.createClass({displayName: 'DesktopRoomConversationView', - mixins: [ActiveRoomStoreMixin, loop.shared.mixins.DocumentTitleMixin], + mixins: [ + ActiveRoomStoreMixin, + sharedMixins.DocumentTitleMixin, + sharedMixins.RoomsAudioMixin + ], propTypes: { dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index 311eea62a0a4..ea8f5535ab97 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -12,6 +12,7 @@ loop.roomViews = (function(mozL10n) { "use strict"; var sharedActions = loop.shared.actions; + var sharedMixins = loop.shared.mixins; var ROOM_STATES = loop.store.ROOM_STATES; var sharedViews = loop.shared.views; @@ -132,7 +133,11 @@ loop.roomViews = (function(mozL10n) { * Desktop room conversation view. */ var DesktopRoomConversationView = React.createClass({ - mixins: [ActiveRoomStoreMixin, loop.shared.mixins.DocumentTitleMixin], + mixins: [ + ActiveRoomStoreMixin, + sharedMixins.DocumentTitleMixin, + sharedMixins.RoomsAudioMixin + ], propTypes: { dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired diff --git a/browser/components/loop/content/shared/js/mixins.js b/browser/components/loop/content/shared/js/mixins.js index 722cd60ff070..01ffa3cfa786 100644 --- a/browser/components/loop/content/shared/js/mixins.js +++ b/browser/components/loop/content/shared/js/mixins.js @@ -226,8 +226,65 @@ loop.shared.mixins = (function() { } }; + /** + * A mixin especially for rooms. This plays the right sound according to + * the state changes. Requires AudioMixin to also be used. + */ + var RoomsAudioMixin = { + mixins: [AudioMixin], + + componentWillUpdate: function(nextProps, nextState) { + var ROOM_STATES = loop.store.ROOM_STATES; + + function isConnectedToRoom(state) { + return state === ROOM_STATES.HAS_PARTICIPANTS || + state === ROOM_STATES.SESSION_CONNECTED; + } + + function notConnectedToRoom(state) { + // Failed and full are states that the user is not + // really connected o the room, but we don't want to + // catch those here, as they get their own sounds. + return state === ROOM_STATES.INIT || + state === ROOM_STATES.GATHER || + state === ROOM_STATES.READY || + state === ROOM_STATES.JOINED; + } + + // Joining the room. + if (notConnectedToRoom(this.state.roomState) && + isConnectedToRoom(nextState.roomState)) { + this.play("room-joined"); + } + + // Other people coming and leaving. + if (this.state.roomState === ROOM_STATES.SESSION_CONNECTED && + nextState.roomState === ROOM_STATES.HAS_PARTICIPANTS) { + this.play("room-joined-in"); + } + + if (this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS && + nextState.roomState === ROOM_STATES.SESSION_CONNECTED) { + this.play("room-left"); + } + + // Leaving the room - same sound as if a participant leaves + if (isConnectedToRoom(this.state.roomState) && + notConnectedToRoom(nextState.roomState)) { + this.play("room-left"); + } + + // Room failures + if (nextState.roomState === ROOM_STATES.FAILED || + nextState.roomState === ROOM_STATES.FULL) { + this.play("failure"); + } + } + }; + return { AudioMixin: AudioMixin, + RoomsAudioMixin: RoomsAudioMixin, setRootObject: setRootObject, DropdownMenuMixin: DropdownMenuMixin, DocumentVisibilityMixin: DocumentVisibilityMixin, diff --git a/browser/components/loop/content/shared/sounds/room-joined-in.ogg b/browser/components/loop/content/shared/sounds/room-joined-in.ogg new file mode 100644 index 0000000000000000000000000000000000000000..164325c63d6072b4de3ca0f2a5cfb476dabf3a2e GIT binary patch literal 9242 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