src/modules/identity-native.js
import Observable from './observable';
/**
* Identity native controller
*
* Allows consumers to send and receive messages via a native application bridge.
* The native application must be set up to create a nativeWrapper window attribute
* to leverage the bridge calls.
*/
class IdentityNative extends Observable {
/**
* IdentityNative constructor
*/
constructor() {
super();
Object.assign(this, {
_commandIdCounter: 1,
_wrapperReady: false,
});
this._wrapperPromise = new Promise((resolve, reject) => {
// Reject after timeout
const timeout = setTimeout(reject, 5000);
this.on('nativeWrapperIsReady', (properties) => {
if (properties && properties.ready) {
this._wrapperReady = true;
clearTimeout(timeout);
return resolve(true);
}
});
}).then(this.onWrapper.bind(this));
this._wrapper = window.nativeWrapper;
if (this._wrapper && this._wrapper.sendMessage) {
this.wrapperReady();
this.sendMessage({type: 'Event', subject: 'AppReady'});
} else {
this._wrapper = window.nativeWrapper = {};
}
// Make sure previous onMessage functions aren't overwritten
if (typeof this._wrapper.onMessage === 'function') {
this._previousOnMessage = this._wrapper.onMessage;
}
this._wrapper.onMessage = this.onMessage.bind(this);
}
/**
* Return if bridge is initialized or not
* @return {boolean}
*/
get initialized() {
return this._initialized;
}
/**
* Initialize the native controller
*
* @return {Promise}
*/
init() {
return new Promise((resolve, reject) => {
this._wrapperPromise.then(resolve, reject);
});
}
/**
* Add callbacks to the onMessage queue
* @param {function} callback
* @param {string} type
*/
subscribe(callback, type = 'CommandResult') {
if (!this._onMessage) this._onMessage = [];
this._onMessage.push({type, callback});
}
/**
* Listen for messages from the native wrapper and add to the event bus
*
* @param {Object} message
*/
onMessage(message) {
// Existing onMessage callback
if (this._previousOnMessage) this._previousOnMessage(message);
const messageType = message.type || 'Event';
const subject = message.subject;
if (!this._wrapperReady && subject === 'BridgeReady') {
this.wrapperReady();
}
if (this._onMessage && this._onMessage.length > 0) {
this._onMessage.forEach(({type, callback}) => {
if (type === messageType) callback(subject, message);
});
}
}
/**
* Initialize the native wrapper
*
* @return {Object}
*/
onWrapper() {
this._wrapper = window.nativeWrapper;
return window.nativeWrapper;
}
/**
* Notify that the wrapper is ready
*/
wrapperReady() {
this.notify('nativeWrapperIsReady', {ready: true});
}
/**
* Send a message to the native wrapper
*
* @param {Object} message
* @return {Promise}
*/
sendMessage(message) {
return this._wrapperPromise.then((wrapper) => wrapper.sendMessage(message));
}
/**
* Send a command to the native wrapper
*
* @param {string} subject
* @param {Object} payload
*/
sendCommand(subject, payload = {}) {
const commandId = this._commandIdCounter++;
this.sendMessage({
type: 'Command',
subject: subject,
commandId: commandId,
payload: payload,
});
}
}
export default IdentityNative;