From cd7248eefdd665bbb9aa03145d2044191be2f141 Mon Sep 17 00:00:00 2001 From: hensm Date: Tue, 30 Apr 2019 17:43:58 +0100 Subject: [PATCH] Use receiver selector manager --- ext/src/main.ts | 254 ++++++++---------- .../ReceiverSelectorManager.ts | 2 +- ext/src/receiverSelectorManager/index.ts | 6 + .../PopupReceiverSelectorManager.ts | 17 +- ext/src/shim/cast/classes/ApiConfig.ts | 6 +- ext/src/shim/cast/index.ts | 33 +-- ext/src/ui/popup/index.tsx | 97 +++---- 7 files changed, 193 insertions(+), 222 deletions(-) diff --git a/ext/src/main.ts b/ext/src/main.ts index 9eb49a6..93ec10b 100755 --- a/ext/src/main.ts +++ b/ext/src/main.ts @@ -7,6 +7,10 @@ import messageRouter from "./lib/messageRouter"; import { getChromeUserAgent } from "./lib/userAgents"; import { getWindowCenteredProps } from "./lib/utils"; +import { ReceiverSelectorMediaType + , ReceiverSelectorSelectedEvent + , PopupReceiverSelectorManager } from "./receiverSelectorManager"; + import { Message, Receiver } from "./types"; import { ReceiverStatusMessage @@ -354,60 +358,83 @@ browser.menus.onClicked.addListener(async (info, tab) => { }); -let popupWinId: number; -let popupShimId: string; -let popupPort: browser.runtime.Port; -/** - * Creates popup window for cast destination selection. - * Refocusing other browser windows causes the popup window - * to close and returns an API error. - */ -async function openPopup (shimId: string) { - // Current window to base centered position on - const win = await browser.windows.getCurrent(); - const centeredProps = getWindowCenteredProps(win, 350, 200); - - const popup = await browser.windows.create({ - url: "ui/popup/index.html" - , type: "popup" - , ...centeredProps - }); - - // Store popup details for message forwarding - popupWinId = popup.id; - popupShimId = shimId; - - // Size/position not set correctly on creation (bug?) - await browser.windows.update(popup.id, { - ...centeredProps - }); - - // Close popup on other browser window focus - browser.windows.onFocusChanged.addListener(function listener (id) { - if (id !== browser.windows.WINDOW_ID_NONE - && id === win.id) { - browser.windows.onFocusChanged.removeListener(listener); - browser.windows.remove(popup.id); - } - }); +interface Shim { + port: browser.runtime.Port; + bridgePort: browser.runtime.Port; + tabId: number; + frameId: number; } -// Track popup close -browser.windows.onRemoved.addListener(id => { - if (id === popupWinId) { - shimMap.get(popupShimId).port.postMessage({ - subject: "shim:/popupClosed" - }); +const shimMap = new Map(); - popupWinId = null; - popupShimId = null; - popupPort = null; +const statusBridge = browser.runtime.connectNative(APPLICATION_NAME); +const statusBridgeReceivers = new Map(); + +statusBridge.onMessage.addListener(async (message: Message) => { + console.log(message); + + switch (message.subject) { + case "shim:/serviceUp": { + const receiver = (message as ServiceUpMessage).data; + statusBridgeReceivers.set(receiver.id, receiver); + + // Forward update to shims + for (const shim of shimMap.values()) { + shim.port.postMessage({ + subject: "shim:/serviceUp" + , data: { id: receiver.id } + }); + } + + break; + } + + case "shim:/serviceDown": { + const { id } = (message as ServiceDownMessage).data; + + if (statusBridgeReceivers.has(id)) { + statusBridgeReceivers.delete(id); + } + + // Forward update to shims + for (const shim of shimMap.values()) { + shim.port.postMessage({ + subject: "shim:/serviceDown" + , data: { id } + }); + } + + break; + } + + case "receiverStatus": { + const { id, status } = (message as ReceiverStatusMessage).data; + + const receiver = statusBridgeReceivers.get(id); + + // Merge new status with old status + statusBridgeReceivers.set(id, { + ...receiver + , status: { + ...receiver.status + , ...status + } + }); + + break; + } + } +}); + +statusBridge.postMessage({ + subject: "bridge:/initialize" + , data: { + shouldWatchStatus: true } }); -const shimMap = new Map(); async function onConnectShim (port: browser.runtime.Port) { const bridgeInfo = await getBridgeInfo(); @@ -471,31 +498,52 @@ async function onConnectShim (port: browser.runtime.Port) { } switch (message.subject) { - case "main:/openPopup": { - /** - * If popup already open, reassign to new shim, - * otherwise create a new popup. - */ - if (popupWinId) { + case "main:/shimInitialized": { - // Reassign popup to new shim - popupPort.postMessage({ - subject: "popup:/assignShim" + // Send existing receivers as serviceUp messages + for (const receiver of statusBridgeReceivers.values()) { + port.postMessage({ + subject: "shim:/serviceUp" + , data: { id: receiver.id } + }); + } + + break; + } + + case "main:/sessionCreated": { + PopupReceiverSelectorManager.close(); + break; + } + + case "main:/selectReceiverBegin": { + PopupReceiverSelectorManager.open( + Array.from(statusBridgeReceivers.values()) + , message.data.defaultMediaType); + + PopupReceiverSelectorManager.addEventListener("selected" + , (ev: ReceiverSelectorSelectedEvent) => { + + port.postMessage({ + subject: "shim:/selectReceiverEnd" , data: { - tabId - , frameId + receiver: ev.detail.receiver } }); + }); - /** - * Notify shim that existing popup has closed and - * to re-populate receiver list for new popup. - */ - port.postMessage({ subject: "shim:/popupClosed" }); - port.postMessage({ subject: "shim:/popupReady" }); - } else { - await openPopup(shimId); - } + PopupReceiverSelectorManager.addEventListener("cancelled", () => { + port.postMessage({ + subject: "shim:/selectReceiverCancelled" + }); + }); + + PopupReceiverSelectorManager.addEventListener("error", () => { + // TODO: Report errors properly + port.postMessage({ + subject: "shim:/selectReceiverCancelled" + }); + }); break; } @@ -514,85 +562,11 @@ async function onConnectShim (port: browser.runtime.Port) { }); } -function onConnectPopup (port: browser.runtime.Port) { - if (popupPort) { - popupPort.disconnect(); - } - - popupPort = port; - - const { tabId, frameId } = shimMap.get(popupShimId); - port.postMessage({ - subject: "popup:/assignShim" - , data: { - tabId - , frameId - } - }); -} - browser.runtime.onConnect.addListener(port => { switch (port.name) { case "shim": onConnectShim(port); break; - case "popup": - onConnectPopup(port); - break; - } -}); - - -const statusBridge = browser.runtime.connectNative(APPLICATION_NAME); -const statusBridgeReceivers = new Map(); - -statusBridge.onMessage.addListener((message: Message) => { - switch (message.subject) { - - case "shim:/serviceUp": { - const serviceUpMessage = message as ServiceUpMessage; - const receiver = serviceUpMessage.data; - - statusBridgeReceivers.set(receiver.id, receiver); - - break; - } - - case "shim:/serviceDown": { - const serviceDownMessage = (message as ServiceDownMessage); - const { id } = serviceDownMessage.data; - - if (statusBridgeReceivers.has(id)) { - statusBridgeReceivers.delete(id); - } - - break; - } - - case "receiverStatus": { - const receiverStatusMessage = message as ReceiverStatusMessage; - const { id, status } = receiverStatusMessage.data; - - const receiver = statusBridgeReceivers.get(id); - - // Merge new status with old status - statusBridgeReceivers.set(id, { - ...receiver - , status: { - ...receiver.status - , ...status - } - }); - - break; - } - } -}); - -statusBridge.postMessage({ - subject: "bridge:/initialize" - , data: { - shouldWatchStatus: true } }); diff --git a/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts b/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts index 3f1a6c1..2d5b902 100644 --- a/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts +++ b/ext/src/receiverSelectorManager/ReceiverSelectorManager.ts @@ -11,7 +11,7 @@ export enum ReceiverSelectorMediaType { export interface ReceiverSelection { receiver: Receiver; - castMethod: ReceiverSelectorMediaType; + mediaType: ReceiverSelectorMediaType; } export type ReceiverSelectorSelectedEvent = CustomEvent; diff --git a/ext/src/receiverSelectorManager/index.ts b/ext/src/receiverSelectorManager/index.ts index c62ea2c..1b19309 100644 --- a/ext/src/receiverSelectorManager/index.ts +++ b/ext/src/receiverSelectorManager/index.ts @@ -1,5 +1,11 @@ "use strict"; +export { ReceiverSelection + , ReceiverSelectorCancelledEvent + , ReceiverSelectorErrorEvent + , ReceiverSelectorMediaType + , ReceiverSelectorSelectedEvent } from "./ReceiverSelectorManager"; + export { default as NativeMacReceiverSelectorManager } from "./selectorManagers/NativeMacReceiverSelectorManager"; diff --git a/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts b/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts index 6518c6a..a2ae890 100644 --- a/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts +++ b/ext/src/receiverSelectorManager/selectorManagers/PopupReceiverSelectorManager.ts @@ -48,7 +48,13 @@ class PopupReceiverSelectorManager this.messagePort = port; this.messagePort.onMessage.addListener(this.onPopupMessage); - // TODO: Send initial data + this.messagePort.postMessage({ + subject: "popup:/populateReceiverList" + , data: { + receivers: this.receivers + , defaultMediaType: this.defaultMediaType + } + }) }); } @@ -97,8 +103,15 @@ class PopupReceiverSelectorManager * Handles popup messages. */ private onPopupMessage (message: Message) { + console.log("popupmsg", message); + switch (message.subject) { - case "selected": { + case "receiverSelectorManager:/selected": { + this.wasReceiverSelected = true; + this.dispatchEvent(new CustomEvent("selected", { + detail: message.data + })); + break; } } diff --git a/ext/src/shim/cast/classes/ApiConfig.ts b/ext/src/shim/cast/classes/ApiConfig.ts index 8b2c52d..687a5e7 100755 --- a/ext/src/shim/cast/classes/ApiConfig.ts +++ b/ext/src/shim/cast/classes/ApiConfig.ts @@ -6,6 +6,9 @@ import SessionRequest from "./SessionRequest"; import { AutoJoinPolicy , DefaultActionPolicy } from "../enums"; +import { ReceiverSelectorMediaType } + from "../../../receiverSelectorManager/ReceiverSelectorManager"; + export default class ApiConfig { public additionalSessionRequests: any[] = []; @@ -23,6 +26,7 @@ export default class ApiConfig { = DefaultActionPolicy.CREATE_SESSION // TODO: Remove awful hack for mirror casting - , public _selectedMedia: string = "app") { + , public _selectedMedia: ReceiverSelectorMediaType + = ReceiverSelectorMediaType.App) { } } diff --git a/ext/src/shim/cast/index.ts b/ext/src/shim/cast/index.ts index eef2082..632ed83 100755 --- a/ext/src/shim/cast/index.ts +++ b/ext/src/shim/cast/index.ts @@ -98,7 +98,7 @@ export function initialize ( apiConfig = newApiConfig; sendMessageResponse({ - subject: "bridge:/startDiscovery" + subject: "main:/shimInitialized" }); apiConfig.receiverListener(receiverList.length @@ -157,7 +157,10 @@ export function requestSession ( // Open destination chooser sendMessageResponse({ - subject: "main:/openPopup" + subject: "main:/selectReceiverBegin" + , data: { + defaultMediaType: apiConfig._selectedMedia + } }); } @@ -186,10 +189,12 @@ export function unescape (escaped: string): string { } -onMessage(message => { +onMessage(async message => { + console.log(message) switch (message.subject) { case "shim:/initialized": { isAvailable = true; + break; } @@ -228,7 +233,7 @@ onMessage(message => { break; } - case "shim:/selectReceiver": { + case "shim:/selectReceiverEnd": { console.info("fx_cast (Debug): Selected receiver"); const selectedReceiver = new Receiver( @@ -247,7 +252,7 @@ onMessage(message => { , selectedReceiver // receiver , (session: Session) => { sendMessageResponse({ - subject: "popup:/close" + subject: "main:/sessionCreated" }); sessionRequestInProgress = false; @@ -272,26 +277,10 @@ onMessage(message => { break; } - /** - * Popup is ready to receive data to populate the cast destination - * chooser. - */ - case "shim:/popupReady": { - sendMessageResponse({ - subject: "popup:/populateReceiverList" - , data: { - receivers: receiverList - , selectedMedia: apiConfig._selectedMedia - } - }); - - break; - } - /** * Popup closed before session established. */ - case "shim:/popupClosed": { + case "shim:/selectReceiverCancelled": { if (sessionRequestInProgress) { sessionRequestInProgress = false; sessionErrorCallback(new Error_(ErrorCode.CANCEL)); diff --git a/ext/src/ui/popup/index.tsx b/ext/src/ui/popup/index.tsx index 231ce51..8564687 100755 --- a/ext/src/ui/popup/index.tsx +++ b/ext/src/ui/popup/index.tsx @@ -7,6 +7,10 @@ import ReactDOM from "react-dom"; import { getNextEllipsis } from "../../lib/utils"; import { Message, Receiver } from "../../types"; +import { ReceiverSelectorMediaType } + from "../../receiverSelectorManager/ReceiverSelectorManager"; + + const _ = browser.i18n.getMessage; // macOS styles @@ -30,7 +34,7 @@ let frameWidth: number; interface PopupAppState { receivers: Receiver[]; - selectedMedia: string; + defaultMediaType: ReceiverSelectorMediaType; isLoading: boolean; } @@ -43,7 +47,7 @@ class PopupApp extends Component<{}, PopupAppState> { this.state = { receivers: [] - , selectedMedia: "app" + , defaultMediaType: ReceiverSelectorMediaType.App , isLoading: false }; @@ -59,28 +63,46 @@ class PopupApp extends Component<{}, PopupAppState> { } public componentDidMount () { - const backgroundPort = browser.runtime.connect({ + this.port = browser.runtime.connect({ name: "popup" }); - backgroundPort.onMessage.addListener((message: Message) => { - if (message.subject === "popup:/assignShim") { - this.setPort(message.data.tabId - , message.data.frameId); + this.port.onMessage.addListener((message: Message) => { + switch (message.subject) { + case "popup:/populateReceiverList": { + this.setState({ + receivers: message.data.receivers + , defaultMediaType: message.data.defaultMediaType + }, () => { + // Get height of content without window decoration + winHeight = document.body.clientHeight + frameHeight; + + browser.windows.update(this.win.id, { + height: winHeight + }); + }); + + break; + } + + case "popup:/close": { + window.close(); + break; + } } }); } public render () { const shareMedia = - this.state.selectedMedia === "tab" - || this.state.selectedMedia === "screen"; + this.state.defaultMediaType === ReceiverSelectorMediaType.Tab + || this.state.defaultMediaType === ReceiverSelectorMediaType.Screen; return (
Cast -