From 9c78a2c62c6b22b43d10955f709c8858595981f7 Mon Sep 17 00:00:00 2001 From: hensm Date: Thu, 11 Apr 2019 09:23:29 +0100 Subject: [PATCH] Add initial ReceiverSelectorManager implementations --- .../ReceiverSelectorManager.ts | 22 +++ ext/src/receiverSelectorManager/index.ts | 7 + .../NativeMacReceiverSelectorManager.ts | 25 +++ .../PopupReceiverSelectorManager.ts | 152 ++++++++++++++++++ ext/src/types.ts | 5 +- 5 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 ext/src/receiverSelectorManager/ReceiverSelectorManager.ts create mode 100644 ext/src/receiverSelectorManager/index.ts create mode 100644 ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts create mode 100644 ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts diff --git a/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts b/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts new file mode 100644 index 0000000..f7d9ea5 --- /dev/null +++ b/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts @@ -0,0 +1,22 @@ +"use strict"; + +import { Receiver } from "../types" + + +export type ReceiverSelectorSelectedEvent = CustomEvent; +export type ReceiverSelectorErrorEvent = CustomEvent; +export type ReceiverSelectorCancelledEvent = CustomEvent; + +export enum ReceiverSelectorCastType { + App + , Tab + , Screen +} + +export default interface ReceiverSelectorManager extends EventTarget { + open ( + receivers: Receiver[] + , defaultCastType: ReceiverSelectorCastType): void; + + close (): void; +} diff --git a/ext/src/receiverSelectorManager/index.ts b/ext/src/receiverSelectorManager/index.ts new file mode 100644 index 0000000..c62ea2c --- /dev/null +++ b/ext/src/receiverSelectorManager/index.ts @@ -0,0 +1,7 @@ +"use strict"; + +export { default as NativeMacReceiverSelectorManager } + from "./selectorManagers/NativeMacReceiverSelectorManager"; + +export { default as PopupReceiverSelectorManager } + from "./selectorManagers/PopupReceiverSelectorManager"; diff --git a/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts b/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts new file mode 100644 index 0000000..8a632d7 --- /dev/null +++ b/ext/src/receiverSelectorManager/selectorManagers/NativeMacReceiverSelectorManager.ts @@ -0,0 +1,25 @@ +"use strict"; + +import ReceiverSelectorManager, { + ReceiverSelectorCastType } from "../ReceiverSelectorManager"; + +import { Receiver, Message } from "../../types"; + + +class NativeMacReceiverSelectorManager + extends EventTarget + implements ReceiverSelectorManager { + + public async open ( + receivers: Receiver[] + , defaultCastType: ReceiverSelectorCastType): Promise { + console.info("STUB :: NativeMacReceiverSelectorManager.open"); + } + + public close (): void { + console.info("STUB :: NativeMacReceiverSelectorManager.close"); + } +} + +// Singleton instance +export default new NativeMacReceiverSelectorManager(); diff --git a/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts b/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts new file mode 100644 index 0000000..c8d233d --- /dev/null +++ b/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts @@ -0,0 +1,152 @@ +"use strict"; + +import ReceiverSelectorManager, { + ReceiverSelectorCastType } from "../ReceiverSelectorManager"; + +import { Receiver, Message } from "../../types"; +import { getWindowCenteredProps } from "../../lib/utils"; + + +class PopupReceiverSelectorManager + extends EventTarget + implements ReceiverSelectorManager { + + private windowId: number; + private openerWindowId: number; + private messagePort: browser.runtime.Port; + + private receivers: Receiver[]; + private defaultCastType: ReceiverSelectorCastType; + + private wasReceiverSelected: boolean = false; + + + constructor () { + super(); + + // Bind methods to pass to addListener + this.onPopupMessage = this.onPopupMessage.bind(this); + this.onWindowsRemoved = this.onWindowsRemoved.bind(this); + this.onWindowsFocusChanged = this.onWindowsFocusChanged.bind(this); + + browser.windows.onRemoved.addListener(this.onWindowsRemoved); + + /** + * Handle incoming message channel connection from popup + * window script. + */ + browser.runtime.onConnect.addListener(port => { + if (port.name !== "popup") { + return; + } + + // Disconnect existing port + if (this.messagePort) { + this.messagePort.disconnect(); + } + + this.messagePort = port; + this.messagePort.onMessage.addListener(this.onPopupMessage); + + // TODO: Send initial data + }); + } + + + public async open ( + receivers: Receiver[] + , defaultCastType: ReceiverSelectorCastType): Promise { + + // If popup already exists, close it + if (this.windowId) { + await browser.windows.remove(this.windowId); + } + + this.receivers = receivers; + this.defaultCastType = defaultCastType; + + // Current window to base centered position on + const openerWindow = await browser.windows.getCurrent(); + const centeredProps = getWindowCenteredProps(openerWindow, 350, 200); + + const popup = await browser.windows.create({ + url: "ui/popup/index.html" + , type: "popup" + , ...centeredProps + }); + + this.windowId = popup.id; + this.openerWindowId = openerWindow.id; + + // Size/position not set correctly on creation (bug?) + await browser.windows.update(this.windowId, { + ...centeredProps + }); + + // Add focus listener + browser.windows.onFocusChanged.addListener( + this.onWindowsFocusChanged); + } + + public close (): void { + browser.windows.remove(this.windowId); + } + + + /** + * Handles popup messages. + */ + private onPopupMessage (message: Message) { + switch (message.subject) { + case "selected": { + break; + } + } + } + + /** + * Handles cancellation state where the popup window is closed + * before a receiver is selected. + */ + private onWindowsRemoved (windowId: number) { + // Only care about popup window + if (windowId !== this.windowId) { + return; + } + + browser.windows.onFocusChanged.removeListener( + this.onWindowsFocusChanged); + + if (!this.wasReceiverSelected) { + this.dispatchEvent(new CustomEvent("cancelled")); + } + + // Cleanup + this.windowId = null; + this.openerWindowId = null; + this.messagePort = null; + this.receivers = null; + this.defaultCastType = null; + this.wasReceiverSelected = false; + } + + /** + * Closes popup window if another browser window is brought + * into focus. Doesn't apply if no window is focused + * `WINDOW_ID_NONE` or if the popup window is re-focused. + */ + private onWindowsFocusChanged (windowId: number) { + if (windowId !== browser.windows.WINDOW_ID_NONE + && windowId !== this.windowId) { + + // Only run once + browser.windows.onFocusChanged.removeListener( + this.onWindowsFocusChanged); + + browser.windows.remove(this.windowId); + } + } +} + +// Singleton instance +export default new PopupReceiverSelectorManager(); diff --git a/ext/src/types.ts b/ext/src/types.ts index a8e72a4..450766e 100644 --- a/ext/src/types.ts +++ b/ext/src/types.ts @@ -7,10 +7,11 @@ export interface Message { } export interface Receiver { - friendlyName: string; address: string; - port: number; currentApp: string; + friendlyName: string; + id: string; + port: number; } export interface DownloadDelta {