import EventEmitter from 'safe-event-emitter'; import ExtensionPlatform from '../platforms/extension'; const NOTIFICATION_HEIGHT = 620; const NOTIFICATION_WIDTH = 360; export const NOTIFICATION_MANAGER_EVENTS = { POPUP_CLOSED: 'onPopupClosed', }; export default class NotificationManager extends EventEmitter { /** * A collection of methods for controlling the showing and hiding of the notification popup. * * @typedef {Object} NotificationManager * */ constructor() { super(); this.platform = new ExtensionPlatform(); this.platform.addOnRemovedListener(this._onWindowClosed.bind(this)); } /** * Either brings an existing MetaMask notification window into focus, or creates a new notification window. New * notification windows are given a 'popup' type. * */ async showPopup() { const popup = await this._getPopup(); // Bring focus to chrome popup if (popup) { // bring focus to existing chrome popup await this.platform.focusWindow(popup.id); } else { let left = 0; let top = 0; try { const lastFocused = await this.platform.getLastFocusedWindow(); // Position window in top right corner of lastFocused window. top = lastFocused.top; left = lastFocused.left + (lastFocused.width - NOTIFICATION_WIDTH); } catch (_) { // The following properties are more than likely 0, due to being // opened from the background chrome process for the extension that // has no physical dimensions const { screenX, screenY, outerWidth } = window; top = Math.max(screenY, 0); left = Math.max(screenX + (outerWidth - NOTIFICATION_WIDTH), 0); } // create new notification popup const popupWindow = await this.platform.openWindow({ url: 'notification.html', type: 'popup', width: NOTIFICATION_WIDTH, height: NOTIFICATION_HEIGHT, left, top, }); // Firefox currently ignores left/top for create, but it works for update if (popupWindow.left !== left && popupWindow.state !== 'fullscreen') { await this.platform.updateWindowPosition(popupWindow.id, left, top); } this._popupId = popupWindow.id; } } _onWindowClosed(windowId) { if (windowId === this._popupId) { this._popupId = undefined; this.emit(NOTIFICATION_MANAGER_EVENTS.POPUP_CLOSED); } } /** * Checks all open MetaMask windows, and returns the first one it finds that is a notification window (i.e. has the * type 'popup') * * @private * @param {Function} cb - A node style callback that to which the found notification window will be passed. * */ async _getPopup() { const windows = await this.platform.getAllWindows(); return this._getPopupIn(windows); } /** * Given an array of windows, returns the 'popup' that has been opened by MetaMask, or null if no such window exists. * * @private * @param {Array} windows - An array of objects containing data about the open MetaMask extension windows. * */ _getPopupIn(windows) { return windows ? windows.find((win) => { // Returns notification popup return win && win.type === 'popup' && win.id === this._popupId; }) : null; } }