Bug 1097703 - Enable install/open FxOS Loop client from standalone UI for ROOM. r=mbanner

This commit is contained in:
Carmen Jimenez 2014-11-23 20:59:03 +01:00
parent 3b5b8cdf05
commit 94e8297c83
21 changed files with 620 additions and 127 deletions

View File

@ -37,6 +37,8 @@
<script type="text/javascript" src="loop/shared/js/store.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/conversationStore.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStates.js"></script>
<script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/feedbackStore.js"></script>
<script type="text/javascript" src="loop/shared/js/feedbackViews.js"></script>

View File

@ -29,6 +29,8 @@
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
<script type="text/javascript" src="loop/shared/js/store.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/roomStates.js"></script>
<script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script>
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="loop/js/client.js"></script>
<script type="text/javascript;version=1.8" src="loop/js/contacts.js"></script>

View File

@ -21,31 +21,7 @@ loop.store.ActiveRoomStore = (function() {
ROOM_FULL: 202
};
var ROOM_STATES = loop.store.ROOM_STATES = {
// The initial state of the room
INIT: "room-init",
// The store is gathering the room data
GATHER: "room-gather",
// The store has got the room data
READY: "room-ready",
// Obtaining media from the user
MEDIA_WAIT: "room-media-wait",
// The room is known to be joined on the loop-server
JOINED: "room-joined",
// The room is connected to the sdk server.
SESSION_CONNECTED: "room-session-connected",
// There are participants in the room.
HAS_PARTICIPANTS: "room-has-participants",
// There was an issue with the room
FAILED: "room-failed",
// The room is full
FULL: "room-full",
// The room conversation has ended
ENDED: "room-ended",
// The window is closing
CLOSING: "room-closing"
};
var ROOM_STATES = loop.store.ROOM_STATES;
/**
* Active room store.
*

View File

@ -0,0 +1,164 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* global loop:true */
var loop = loop || {};
loop.store = loop.store || {};
loop.store.FxOSActiveRoomStore = (function() {
"use strict";
var sharedActions = loop.shared.actions;
var ROOM_STATES = loop.store.ROOM_STATES;
var FxOSActiveRoomStore = loop.store.createStore({
actions: [
"fetchServerData"
],
initialize: function(options) {
if (!options.mozLoop) {
throw new Error("Missing option mozLoop");
}
this._mozLoop = options.mozLoop;
},
/**
* Returns initial state data for this active room.
*/
getInitialStoreState: function() {
return {
roomState: ROOM_STATES.INIT,
audioMuted: false,
videoMuted: false,
failureReason: undefined
};
},
/**
* Registers the actions with the dispatcher that this store is interested
* in after the initial setup has been performed.
*/
_registerPostSetupActions: function() {
this.dispatcher.register(this, [
"joinRoom"
]);
},
/**
* Execute fetchServerData event action from the dispatcher. Although
* this is to fetch the server data - for rooms on the standalone client,
* we don't actually need to get any data. Therefore we just save the
* data that is given to us for when the user chooses to join the room.
*
* @param {sharedActions.FetchServerData} actionData
*/
fetchServerData: function(actionData) {
if (actionData.windowType !== "room") {
// Nothing for us to do here, leave it to other stores.
return;
}
this._registerPostSetupActions();
this.setStoreState({
roomToken: actionData.token,
roomState: ROOM_STATES.READY
});
},
/**
* Handles the action to join to a room.
*/
joinRoom: function() {
// Reset the failure reason if necessary.
if (this.getStoreState().failureReason) {
this.setStoreState({failureReason: undefined});
}
this._setupOutgoingRoom(true);
},
/**
* Sets up an outgoing room. It will try launching the activity to let the
* FirefoxOS loop app handle the call. If the activity fails:
* - if installApp is true, then it'll try to install the FirefoxOS loop
* app.
* - if installApp is false, then it'll just log and error and fail.
*
* @param {boolean} installApp
*/
_setupOutgoingRoom: function(installApp) {
var request = new MozActivity({
name: "room-call",
data: {
type: "loop/rToken",
token: this.getStoreState("roomToken")
}
});
request.onsuccess = function() {};
request.onerror = (function(event) {
if (!installApp) {
// This really should not happen ever.
console.error(
"Unexpected activity launch error after the app has been installed");
return;
}
if (event.target.error.name !== "NO_PROVIDER") {
console.error ("Unexpected " + event.target.error.name);
return;
}
// We need to install the FxOS app.
this.setStoreState({
marketplaceSrc: loop.config.marketplaceUrl,
onMarketplaceMessage: this._onMarketplaceMessage.bind(this)
});
}).bind(this);
},
/**
* This method will handle events generated on the marketplace frame. It
* will launch the FirefoxOS loop app installation, and receive the result
* of the installation.
*
* @param {DOMEvent} event
*/
_onMarketplaceMessage: function(event) {
var message = event.data;
switch (message.name) {
case "loaded":
var marketplace = window.document.getElementById("marketplace");
// Once we have it loaded, we request the installation of the FxOS
// Loop client app. We will be receiving the result of this action
// via postMessage from the child iframe.
marketplace.contentWindow.postMessage({
"name": "install-package",
"data": {
"product": {
"name": loop.config.fxosApp.name,
"manifest_url": loop.config.fxosApp.manifestUrl,
"is_packaged": true
}
}
}, "*");
break;
case "install-package":
window.removeEventListener("message", this.onMarketplaceMessage);
if (message.error) {
console.error(message.error.error);
return;
}
// We installed the FxOS app, so we can continue with the call
// process.
this._setupOutgoingRoom(false);
break;
}
}
});
return FxOSActiveRoomStore;
})();

View File

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* global loop:true */
var loop = loop || {};
loop.store = loop.store || {};
loop.store.ROOM_STATES = {
// The initial state of the room
INIT: "room-init",
// The store is gathering the room data
GATHER: "room-gather",
// The store has got the room data
READY: "room-ready",
// Obtaining media from the user
MEDIA_WAIT: "room-media-wait",
// The room is known to be joined on the loop-server
JOINED: "room-joined",
// The room is connected to the sdk server.
SESSION_CONNECTED: "room-session-connected",
// There are participants in the room.
HAS_PARTICIPANTS: "room-has-participants",
// There was an issue with the room
FAILED: "room-failed",
// The room is full
FULL: "room-full",
// The room conversation has ended
ENDED: "room-ended",
// The window is closing
CLOSING: "room-closing"
};

View File

@ -66,22 +66,24 @@ browser.jar:
content/browser/loop/shared/img/telefonica@2x.png (content/shared/img/telefonica@2x.png)
# Shared scripts
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js)
content/browser/loop/shared/js/store.js (content/shared/js/store.js)
content/browser/loop/shared/js/roomStore.js (content/shared/js/roomStore.js)
content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js)
content/browser/loop/shared/js/feedbackStore.js (content/shared/js/feedbackStore.js)
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
content/browser/loop/shared/js/otSdkDriver.js (content/shared/js/otSdkDriver.js)
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
content/browser/loop/shared/js/feedbackViews.js (content/shared/js/feedbackViews.js)
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
content/browser/loop/shared/js/websocket.js (content/shared/js/websocket.js)
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js)
content/browser/loop/shared/js/store.js (content/shared/js/store.js)
content/browser/loop/shared/js/roomStore.js (content/shared/js/roomStore.js)
content/browser/loop/shared/js/roomStates.js (content/shared/js/roomStates.js)
content/browser/loop/shared/js/fxOSActiveRoomStore.js (content/shared/js/fxOSActiveRoomStore.js)
content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js)
content/browser/loop/shared/js/feedbackStore.js (content/shared/js/feedbackStore.js)
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
content/browser/loop/shared/js/otSdkDriver.js (content/shared/js/otSdkDriver.js)
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
content/browser/loop/shared/js/feedbackViews.js (content/shared/js/feedbackViews.js)
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
content/browser/loop/shared/js/websocket.js (content/shared/js/websocket.js)
# Shared libs
#ifdef DEBUG

View File

@ -83,6 +83,7 @@ config:
@echo "loop.config.learnMoreUrl = '`echo $(LOOP_PRODUCT_HOMEPAGE_URL)`';" >> content/config.js
@echo "loop.config.fxosApp = loop.config.fxosApp || {};" >> content/config.js
@echo "loop.config.fxosApp.name = 'Loop';" >> content/config.js
@echo "loop.config.fxosApp.rooms = true;" >> content/config.js
@echo "loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';" >> content/config.js
@echo "loop.config.roomsSupportUrl = 'https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc';" >> content/config.js
@echo "loop.config.guestSupportUrl = 'https://support.mozilla.org/kb/respond-firefox-hello-invitation-guest-mode';" >> content/config.js

View File

@ -98,12 +98,15 @@
<script type="text/javascript" src="shared/js/websocket.js"></script>
<script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
<script type="text/javascript" src="shared/js/store.js"></script>
<script type="text/javascript" src="shared/js/roomStates.js"></script>
<script type="text/javascript" src="shared/js/fxOSActiveRoomStore.js"></script>
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
<script type="text/javascript" src="shared/js/feedbackStore.js"></script>
<script type="text/javascript" src="shared/js/feedbackViews.js"></script>
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
<script type="text/javascript" src="js/standaloneClient.js"></script>
<script type="text/javascript" src="js/standaloneMozLoop.js"></script>
<script type="text/javascript" src="js/fxOSMarketplace.js"></script>
<script type="text/javascript" src="js/standaloneRoomViews.js"></script>
<script type="text/javascript" src="js/webapp.js"></script>

View File

@ -0,0 +1,37 @@
/** @jsx React.DOM */
var loop = loop || {};
loop.fxOSMarketplaceViews = (function() {
"use strict";
/**
* The Firefox Marketplace exposes a web page that contains a postMesssage
* based API that wraps a small set of functionality from the WebApps API
* that allow us to request the installation of apps given their manifest
* URL. We will be embedding the content of this web page within an hidden
* iframe in case that we need to request the installation of the FxOS Loop
* client.
*/
var FxOSHiddenMarketplaceView = React.createClass({displayName: 'FxOSHiddenMarketplaceView',
render: function() {
return React.DOM.iframe({id: "marketplace", src: this.props.marketplaceSrc, hidden: true});
},
componentDidUpdate: function() {
// This happens only once when we change the 'src' property of the iframe.
if (this.props.onMarketplaceMessage) {
// The reason for listening on the global window instead of on the
// iframe content window is because the Marketplace is doing a
// window.top.postMessage.
// XXX Bug 1097703: This should be changed to an action when the old
// style URLs go away.
window.addEventListener("message", this.props.onMarketplaceMessage);
}
}
});
return {
FxOSHiddenMarketplaceView: FxOSHiddenMarketplaceView
};
})();

View File

@ -0,0 +1,37 @@
/** @jsx React.DOM */
var loop = loop || {};
loop.fxOSMarketplaceViews = (function() {
"use strict";
/**
* The Firefox Marketplace exposes a web page that contains a postMesssage
* based API that wraps a small set of functionality from the WebApps API
* that allow us to request the installation of apps given their manifest
* URL. We will be embedding the content of this web page within an hidden
* iframe in case that we need to request the installation of the FxOS Loop
* client.
*/
var FxOSHiddenMarketplaceView = React.createClass({
render: function() {
return <iframe id="marketplace" src={this.props.marketplaceSrc} hidden/>;
},
componentDidUpdate: function() {
// This happens only once when we change the 'src' property of the iframe.
if (this.props.onMarketplaceMessage) {
// The reason for listening on the global window instead of on the
// iframe content window is because the Marketplace is doing a
// window.top.postMessage.
// XXX Bug 1097703: This should be changed to an action when the old
// style URLs go away.
window.addEventListener("message", this.props.onMarketplaceMessage);
}
}
});
return {
FxOSHiddenMarketplaceView: FxOSHiddenMarketplaceView
};
})();

View File

@ -20,8 +20,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
var StandaloneRoomInfoArea = React.createClass({displayName: 'StandaloneRoomInfoArea',
propTypes: {
helper: React.PropTypes.instanceOf(loop.shared.utils.Helper).isRequired,
activeRoomStore:
React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired,
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
]).isRequired,
feedbackStore:
React.PropTypes.instanceOf(loop.store.FeedbackStore).isRequired
},
@ -189,8 +191,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
],
propTypes: {
activeRoomStore:
React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired,
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
]).isRequired,
feedbackStore:
React.PropTypes.instanceOf(loop.store.FeedbackStore).isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
@ -379,6 +383,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
enableHangup: this._roomIsActive()})
)
),
loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView({
marketplaceSrc: this.state.marketplaceSrc,
onMarketplaceMessage: this.state.onMarketplaceMessage}),
StandaloneRoomFooter(null)
)
);

View File

@ -20,8 +20,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
var StandaloneRoomInfoArea = React.createClass({
propTypes: {
helper: React.PropTypes.instanceOf(loop.shared.utils.Helper).isRequired,
activeRoomStore:
React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired,
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
]).isRequired,
feedbackStore:
React.PropTypes.instanceOf(loop.store.FeedbackStore).isRequired
},
@ -189,8 +191,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
],
propTypes: {
activeRoomStore:
React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired,
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
]).isRequired,
feedbackStore:
React.PropTypes.instanceOf(loop.store.FeedbackStore).isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
@ -379,6 +383,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
enableHangup={this._roomIsActive()} />
</div>
</div>
<loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView
marketplaceSrc={this.state.marketplaceSrc}
onMarketplaceMessage={this.state.onMarketplaceMessage} />
<StandaloneRoomFooter />
</div>
);

View File

@ -127,30 +127,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
}
});
/**
* The Firefox Marketplace exposes a web page that contains a postMesssage
* based API that wraps a small set of functionality from the WebApps API
* that allow us to request the installation of apps given their manifest
* URL. We will be embedding the content of this web page within an hidden
* iframe in case that we need to request the installation of the FxOS Loop
* client.
*/
var FxOSHiddenMarketplace = React.createClass({displayName: 'FxOSHiddenMarketplace',
render: function() {
return React.DOM.iframe({id: "marketplace", src: this.props.marketplaceSrc, hidden: true});
},
componentDidUpdate: function() {
// This happens only once when we change the 'src' property of the iframe.
if (this.props.onMarketplaceMessage) {
// The reason for listening on the global window instead of on the
// iframe content window is because the Marketplace is doing a
// window.top.postMessage.
window.addEventListener("message", this.props.onMarketplaceMessage);
}
}
});
var FxOSConversationModel = Backbone.Model.extend({
setupOutgoingCall: function(selectedCallType) {
if (selectedCallType) {
@ -568,7 +544,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
dangerouslySetInnerHTML: {__html: tosHTML}})
),
FxOSHiddenMarketplace({
loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView({
marketplaceSrc: this.state.marketplaceSrc,
onMarketplaceMessage: this.state.onMarketplaceMessage}),
@ -962,8 +938,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
// XXX New types for flux style
standaloneAppStore: React.PropTypes.instanceOf(
loop.store.StandaloneAppStore).isRequired,
activeRoomStore: React.PropTypes.instanceOf(
loop.store.ActiveRoomStore).isRequired,
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
]).isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
},
@ -1039,14 +1017,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
// Older non-flux based items.
var notifications = new sharedModels.NotificationCollection();
var conversation
if (helper.isFirefoxOS(navigator.userAgent)) {
conversation = new FxOSConversationModel();
} else {
conversation = new sharedModels.ConversationModel({}, {
sdk: OT
});
}
var feedbackApiClient = new loop.FeedbackAPIClient(
loop.config.feedbackApiUrl, {
@ -1064,6 +1034,29 @@ loop.webapp = (function($, _, OT, mozL10n) {
dispatcher: dispatcher,
sdk: OT
});
var conversation;
var activeRoomStore;
if (helper.isFirefoxOS(navigator.userAgent)) {
if (loop.config.fxosApp) {
conversation = new FxOSConversationModel();
if (loop.config.fxosApp.rooms) {
activeRoomStore = new loop.store.FxOSActiveRoomStore(dispatcher, {
mozLoop: standaloneMozLoop
});
}
}
}
conversation = conversation ||
new sharedModels.ConversationModel({}, {
sdk: OT
});
activeRoomStore = activeRoomStore ||
new loop.store.ActiveRoomStore(dispatcher, {
mozLoop: standaloneMozLoop,
sdkDriver: sdkDriver
});
var feedbackClient = new loop.FeedbackAPIClient(
loop.config.feedbackApiUrl, {
product: loop.config.feedbackProductName,
@ -1078,10 +1071,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
helper: helper,
sdk: OT
});
var activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
mozLoop: standaloneMozLoop,
sdkDriver: sdkDriver
});
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
feedbackClient: feedbackClient
});

View File

@ -127,30 +127,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
}
});
/**
* The Firefox Marketplace exposes a web page that contains a postMesssage
* based API that wraps a small set of functionality from the WebApps API
* that allow us to request the installation of apps given their manifest
* URL. We will be embedding the content of this web page within an hidden
* iframe in case that we need to request the installation of the FxOS Loop
* client.
*/
var FxOSHiddenMarketplace = React.createClass({
render: function() {
return <iframe id="marketplace" src={this.props.marketplaceSrc} hidden/>;
},
componentDidUpdate: function() {
// This happens only once when we change the 'src' property of the iframe.
if (this.props.onMarketplaceMessage) {
// The reason for listening on the global window instead of on the
// iframe content window is because the Marketplace is doing a
// window.top.postMessage.
window.addEventListener("message", this.props.onMarketplaceMessage);
}
}
});
var FxOSConversationModel = Backbone.Model.extend({
setupOutgoingCall: function(selectedCallType) {
if (selectedCallType) {
@ -568,7 +544,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
dangerouslySetInnerHTML={{__html: tosHTML}}></p>
</div>
<FxOSHiddenMarketplace
<loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView
marketplaceSrc={this.state.marketplaceSrc}
onMarketplaceMessage= {this.state.onMarketplaceMessage} />
@ -962,8 +938,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
// XXX New types for flux style
standaloneAppStore: React.PropTypes.instanceOf(
loop.store.StandaloneAppStore).isRequired,
activeRoomStore: React.PropTypes.instanceOf(
loop.store.ActiveRoomStore).isRequired,
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
]).isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
feedbackStore: React.PropTypes.instanceOf(loop.store.FeedbackStore)
},
@ -1039,14 +1017,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
// Older non-flux based items.
var notifications = new sharedModels.NotificationCollection();
var conversation
if (helper.isFirefoxOS(navigator.userAgent)) {
conversation = new FxOSConversationModel();
} else {
conversation = new sharedModels.ConversationModel({}, {
sdk: OT
});
}
var feedbackApiClient = new loop.FeedbackAPIClient(
loop.config.feedbackApiUrl, {
@ -1064,6 +1034,29 @@ loop.webapp = (function($, _, OT, mozL10n) {
dispatcher: dispatcher,
sdk: OT
});
var conversation;
var activeRoomStore;
if (helper.isFirefoxOS(navigator.userAgent)) {
if (loop.config.fxosApp) {
conversation = new FxOSConversationModel();
if (loop.config.fxosApp.rooms) {
activeRoomStore = new loop.store.FxOSActiveRoomStore(dispatcher, {
mozLoop: standaloneMozLoop
});
}
}
}
conversation = conversation ||
new sharedModels.ConversationModel({}, {
sdk: OT
});
activeRoomStore = activeRoomStore ||
new loop.store.ActiveRoomStore(dispatcher, {
mozLoop: standaloneMozLoop,
sdkDriver: sdkDriver
});
var feedbackClient = new loop.FeedbackAPIClient(
loop.config.feedbackApiUrl, {
product: loop.config.feedbackProductName,
@ -1078,10 +1071,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
helper: helper,
sdk: OT
});
var activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
mozLoop: standaloneMozLoop,
sdkDriver: sdkDriver
});
var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
feedbackClient: feedbackClient
});

View File

@ -30,6 +30,7 @@ function getConfigFile(req, res) {
"loop.config.legalWebsiteUrl = 'https://www.mozilla.org/about/legal/terms/firefox-hello/';",
"loop.config.fxosApp = loop.config.fxosApp || {};",
"loop.config.fxosApp.name = 'Loop';",
"loop.config.fxosApp.rooms = true;",
"loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';",
"loop.config.roomsSupportUrl = 'https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc';",
"loop.config.guestSupportUrl = 'https://support.mozilla.org/kb/respond-firefox-hello-invitation-guest-mode';",

View File

@ -52,6 +52,8 @@
<script src="../../content/shared/js/store.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script>
<script src="../../content/shared/js/roomStore.js"></script>
<script src="../../content/shared/js/roomStates.js"></script>
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script>
<script src="../../content/shared/js/feedbackStore.js"></script>
<script src="../../content/shared/js/feedbackViews.js"></script>

View File

@ -0,0 +1,212 @@
/* global chai, loop */
var expect = chai.expect;
var sharedActions = loop.shared.actions;
describe("loop.store.FxOSActiveRoomStore", function () {
"use strict";
var ROOM_STATES = loop.store.ROOM_STATES;
var sandbox;
var dispatcher;
var fakeMozLoop;
var store;
beforeEach(function() {
sandbox = sinon.sandbox.create();
sandbox.useFakeTimers();
dispatcher = new loop.Dispatcher();
sandbox.stub(dispatcher, "dispatch");
fakeMozLoop = {
setLoopPref: sandbox.stub(),
rooms: {
join: sinon.stub()
}
};
store = new loop.store.FxOSActiveRoomStore(dispatcher, {
mozLoop: fakeMozLoop
});
});
afterEach(function() {
sandbox.restore();
});
describe("#FxOSActiveRoomStore - constructor", function() {
it("should throw an error if mozLoop is missing", function() {
expect(function() {
new loop.store.FxOSActiveRoomStore(dispatcher);
}).to.Throw(/mozLoop/);
});
});
describe("#FxOSActiveRoomStore - fetchServerData", function() {
it("should save the token", function() {
store.fetchServerData(new sharedActions.FetchServerData({
windowType: "room",
token: "fakeToken"
}));
expect(store.getStoreState().roomToken).eql("fakeToken");
});
it("should set the state to `READY`", function() {
store.fetchServerData(new sharedActions.FetchServerData({
windowType: "room",
token: "fakeToken"
}));
expect(store.getStoreState().roomState).eql(ROOM_STATES.READY);
});
});
describe("#FxOSActiveRoomStore - setupOutgoingRoom", function() {
var realMozActivity;
var _activityDetail;
var _onerror;
function fireError(errorName) {
_onerror({
target: {
error: {
name: errorName
}
}
});
}
before(function() {
realMozActivity = window.MozActivity;
window.MozActivity = function(activityDetail) {
_activityDetail = activityDetail;
return {
set onerror(cbk) {
_onerror = cbk;
}
};
};
});
after(function() {
window.MozActivity = realMozActivity;
});
beforeEach(function() {
sandbox.stub(console, "error");
_activityDetail = undefined;
_onerror = undefined;
});
afterEach(function() {
sandbox.restore();
});
it("should reset failureReason", function() {
store.setStoreState({failureReason: "Test"});
store.joinRoom();
expect(store.getStoreState().failureReason).eql(undefined);
});
it("should create an activity", function() {
store.setStoreState({
roomToken: "fakeToken",
token: "fakeToken"
});
expect(_activityDetail).to.not.exist;
store.joinRoom();
expect(_activityDetail).to.exist;
expect(_activityDetail).eql({
name: "room-call",
data: {
type: "loop/rToken",
token: "fakeToken"
}
});
});
it("should change the store state when the activity fail with a " +
"NO_PROVIDER error", function() {
loop.config = {
marketplaceUrl: "http://market/"
};
store._setupOutgoingRoom(true);
fireError("NO_PROVIDER");
expect(store.getStoreState().marketplaceSrc).eql(
loop.config.marketplaceUrl
);
});
it("should log an error when the activity fail with a error different " +
"from NO_PROVIDER", function() {
loop.config = {
marketplaceUrl: "http://market/"
};
store._setupOutgoingRoom(true);
fireError("whatever");
sinon.assert.calledOnce(console.error);
sinon.assert.calledWith(console.error, "Unexpected whatever");
});
it("should log an error and exist if an activity error is received when " +
"the parameter is false ", function() {
loop.config = {
marketplaceUrl: "http://market/"
};
store._setupOutgoingRoom(false);
fireError("whatever");
sinon.assert.calledOnce(console.error);
sinon.assert.calledWith(console.error,
"Unexpected activity launch error after the app has been installed");
});
});
describe("#FxOSActiveRoomStore - _onMarketplaceMessage", function() {
var setupOutgoingRoom;
beforeEach(function() {
sandbox.stub(console, "error");
setupOutgoingRoom = sandbox.stub(store, "_setupOutgoingRoom");
});
afterEach(function() {
setupOutgoingRoom.restore();
});
it("We should call trigger a FxOS outgoing call if we get " +
"install-package message without error", function() {
sinon.assert.notCalled(setupOutgoingRoom);
store._onMarketplaceMessage({
data: {
name: "install-package"
}
});
sinon.assert.calledOnce(setupOutgoingRoom);
});
it("We should log an error if we get install-package message with an " +
"error", function() {
sinon.assert.notCalled(setupOutgoingRoom);
store._onMarketplaceMessage({
data: {
name: "install-package",
error: { error: "whatever error" }
}
});
sinon.assert.notCalled(setupOutgoingRoom);
sinon.assert.calledOnce(console.error);
sinon.assert.calledWith(console.error, "whatever error");
});
});
});

View File

@ -50,6 +50,8 @@
<script src="../../content/shared/js/dispatcher.js"></script>
<script src="../../content/shared/js/otSdkDriver.js"></script>
<script src="../../content/shared/js/store.js"></script>
<script src="../../content/shared/js/roomStates.js"></script>
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script>
<script src="../../content/shared/js/roomStore.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script>
@ -67,6 +69,7 @@
<script src="validate_test.js"></script>
<script src="dispatcher_test.js"></script>
<script src="activeRoomStore_test.js"></script>
<script src="fxOSActiveRoomStore_test.js"></script>
<script src="conversationStore_test.js"></script>
<script src="feedbackStore_test.js"></script>
<script src="otSdkDriver_test.js"></script>

View File

@ -48,6 +48,8 @@
<script src="../../content/shared/js/validate.js"></script>
<script src="../../content/shared/js/dispatcher.js"></script>
<script src="../../content/shared/js/store.js"></script>
<script src="../../content/shared/js/roomStates.js"></script>
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script>
<script src="../../content/shared/js/feedbackStore.js"></script>
<script src="../../content/shared/js/feedbackViews.js"></script>
@ -56,6 +58,7 @@
<script src="../../standalone/content/js/standaloneAppStore.js"></script>
<script src="../../standalone/content/js/standaloneClient.js"></script>
<script src="../../standalone/content/js/standaloneMozLoop.js"></script>
<script src="../../standalone/content/js/fxOSMarketplace.js"></script>
<script src="../../standalone/content/js/standaloneRoomViews.js"></script>
<script src="../../standalone/content/js/webapp.js"></script>
<!-- Test scripts -->

View File

@ -332,6 +332,26 @@ describe("loop.standaloneRoomViews", function() {
.not.eql(null);
});
});
describe("Marketplace hidden iframe", function() {
it("should set src when the store state change",
function(done) {
var marketplace = view.getDOMNode().querySelector("#marketplace");
expect(marketplace.src).to.be.equal("");
activeRoomStore.setStoreState({
marketplaceSrc: "http://market/",
onMarketplaceMessage: function () {}
});
view.forceUpdate(function() {
expect(marketplace.src).to.be.equal("http://market/");
done();
});
});
});
});
});
});

View File

@ -44,12 +44,15 @@
<script src="../content/shared/js/store.js"></script>
<script src="../content/shared/js/roomStore.js"></script>
<script src="../content/shared/js/conversationStore.js"></script>
<script src="../content/shared/js/roomStates.js"></script>
<script src="../content/shared/js/fxOSActiveRoomStore.js"></script>
<script src="../content/shared/js/activeRoomStore.js"></script>
<script src="../content/shared/js/feedbackStore.js"></script>
<script src="../content/shared/js/feedbackViews.js"></script>
<script src="../content/js/roomViews.js"></script>
<script src="../content/js/conversationViews.js"></script>
<script src="../content/js/client.js"></script>
<script src="../content/js/fxOSMarketplace.js"></script>
<script src="../content/js/webapp.js"></script>
<script src="../content/js/standaloneRoomViews.js"></script>
<script type="text/javascript;version=1.8" src="../content/js/contacts.js"></script>