Files
tubestation/browser/extensions/activity-stream/lib/MessageCenterRouter.jsm
2018-03-29 11:21:54 -07:00

164 lines
5.1 KiB
JavaScript

const INCOMING_MESSAGE_NAME = "MessageCenter:child-to-parent";
const OUTGOING_MESSAGE_NAME = "MessageCenter:parent-to-child";
const FAKE_MESSAGES = [
{
id: "ONBOARDING_1",
template: "simple_snippet",
content: {
title: "Find it faster",
body: "Access all of your favorite search engines with a click. Search the whole Web or just one website from the search box.",
button: {
label: "Learn More",
action: "OPEN_LINK",
params: {url: "https://mozilla.org"}
}
}
},
{
id: "ONBOARDING_2",
template: "simple_snippet",
content: {
title: "Make Firefox your go-to-browser",
body: "It doesn't take much to get the most from Firefox. Just set Firefox as your default browser and put control, customization, and protection on autopilot."
}
},
{
id: "ONBOARDING_3",
template: "simple_snippet",
content: {
title: "Did you know?",
body: "All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood."
}
}
];
/**
* getRandomItemFromArray
*
* @param {Array} arr An array of items
* @returns one of the items in the array
*/
function getRandomItemFromArray(arr) {
const index = Math.floor(Math.random() * arr.length);
return arr[index];
}
/**
* @class _MessageCenterRouter - Keeps track of all messages, UI surfaces, and
* handles blocking, rotation, etc. Inspecting MessageCenter.state will
* tell you what the current displayed message is in all UI surfaces.
*
* Note: This is written as a constructor rather than just a plain object
* so that it can be more easily unit tested.
*/
class _MessageCenterRouter {
constructor(initialState = {}) {
this.initialized = false;
this.messageChannel = null;
this._state = Object.assign({
currentId: null,
blockList: {},
messages: {}
}, initialState);
this.onMessage = this.onMessage.bind(this);
}
get state() {
return this._state;
}
set state(value) {
throw new Error("Do not modify this.state directy. Instead, call this.setState(newState)");
}
/**
* init - Initializes the MessageRouter.
* It is ready when it has been connected to a RemotePageManager instance.
*
* @param {RemotePageManager} channel a RemotePageManager instance
* @memberof _MessageCenterRouter
*/
init(channel) {
this.messageChannel = channel;
this.messageChannel.addMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
this.initialized = true;
}
uninit() {
this.messageChannel.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "CLEAR_MESSAGE"});
this.messageChannel.removeMessageListener(INCOMING_MESSAGE_NAME, this.onMessage);
this.messageChannel = null;
this.initialized = false;
}
setState(callbackOrObj) {
const newState = (typeof callbackOrObj === "function") ? callbackOrObj(this.state) : callbackOrObj;
this._state = Object.assign({}, this.state, newState);
return new Promise(resolve => {
this._onStateChanged(this.state);
resolve();
});
}
_onStateChanged(state) {
this.messageChannel.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "ADMIN_SET_STATE", data: state});
}
async sendNextMessage(target, id) {
let message;
await this.setState(state => {
message = getRandomItemFromArray(state.messages.filter(item => item.id !== state.currentId && !state.blockList[item.id]));
return {currentId: message ? message.id : null};
});
if (message) {
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "SET_MESSAGE", data: message});
} else {
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "CLEAR_MESSAGE"});
}
}
async clearMessage(target, id) {
if (this.state.currentId === id) {
await this.setState({currentId: null});
}
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "CLEAR_MESSAGE"});
}
async onMessage({data: action, target}) {
switch (action.type) {
case "CONNECT_UI_REQUEST":
case "GET_NEXT_MESSAGE":
await this.sendNextMessage(target);
break;
case "BLOCK_MESSAGE_BY_ID":
await this.setState(state => {
const newState = Object.assign({}, state.blockList);
newState[action.data.id] = true;
return {blockList: newState};
});
await this.clearMessage(target, action.data.id);
break;
case "UNBLOCK_MESSAGE_BY_ID":
await this.setState(state => {
const newState = Object.assign({}, state.blockList);
delete newState[action.data.id];
return {blockList: newState};
});
break;
case "ADMIN_CONNECT_STATE":
target.sendAsyncMessage(OUTGOING_MESSAGE_NAME, {type: "ADMIN_SET_STATE", data: this.state});
break;
}
}
}
this._MessageCenterRouter = _MessageCenterRouter;
/**
* MessageCenterRouter - singleton instance of _MessageCenterRouter that controls all messages
* in the new tab page.
*/
this.MessageCenterRouter = new _MessageCenterRouter({messages: FAKE_MESSAGES});
const EXPORTED_SYMBOLS = ["_MessageCenterRouter", "MessageCenterRouter"];