Minor receiver selector refactoring

This commit is contained in:
hensm
2022-04-28 13:58:34 +01:00
parent dececa46c3
commit 70ac18511a
2 changed files with 90 additions and 117 deletions

View File

@@ -16,13 +16,6 @@ import {
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
interface ReceiverSelectorEvents {
selected: ReceiverSelectionCast;
error: string;
cancelled: void;
stop: ReceiverSelectionStop;
}
export interface PageInfo {
url: string;
tabId: number;
@@ -30,17 +23,27 @@ export interface PageInfo {
sessionRequest?: SessionRequest;
}
interface ReceiverSelectorEvents {
selected: ReceiverSelectionCast;
error: string;
cancelled: void;
stop: ReceiverSelectionStop;
}
/**
* Manages the receiver selector popup window and communication with the
* extension page hosted within.
*/
export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorEvents> {
/** Popup window ID. */
private windowId?: number;
/** Message port to extension page within popup window. */
private messagePort?: Port;
private messagePortDisconnected?: boolean;
private receiverDevices?: ReceiverDevice[];
private defaultMediaType?: ReceiverSelectorMediaType;
private availableMediaTypes?: ReceiverSelectorMediaType;
@@ -68,6 +71,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
messaging.onConnect.addListener(this.onConnect);
}
/** Is receiver selector window currently open. */
get isOpen() {
return this.#isOpen;
}
@@ -75,24 +79,24 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
/**
* Creates and opens a receiver selector window.
*/
public async open(
receiverDevices: ReceiverDevice[],
defaultMediaType: ReceiverSelectorMediaType,
availableMediaTypes: ReceiverSelectorMediaType,
appId?: string,
pageInfo?: PageInfo
) {
this.appId = appId;
this.pageInfo = pageInfo;
public async open(opts: {
receiverDevices: ReceiverDevice[];
defaultMediaType: ReceiverSelectorMediaType;
availableMediaTypes: ReceiverSelectorMediaType;
appId?: string;
pageInfo?: PageInfo;
}) {
this.appId = opts.appId;
this.pageInfo = opts.pageInfo;
// If popup already exists, close it
if (this.windowId) {
await browser.windows.remove(this.windowId);
}
this.receiverDevices = receiverDevices;
this.defaultMediaType = defaultMediaType;
this.availableMediaTypes = availableMediaTypes;
this.receiverDevices = opts.receiverDevices;
this.defaultMediaType = opts.defaultMediaType;
this.availableMediaTypes = opts.availableMediaTypes;
const popupSizePosition = {
width: 350,
@@ -163,7 +167,6 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
await browser.windows.remove(this.windowId);
}
this.#isOpen = false;
this.appId = undefined;
if (this.messagePort && !this.messagePortDisconnected) {
@@ -255,6 +258,8 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
return;
}
this.#isOpen = false;
browser.windows.onRemoved.removeListener(this.onWindowsRemoved);
browser.windows.onFocusChanged.removeListener(
this.onWindowsFocusChanged
@@ -265,11 +270,11 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
}
// Cleanup
this.windowId = undefined;
this.messagePort = undefined;
this.receiverDevices = undefined;
this.defaultMediaType = undefined;
this.availableMediaTypes = undefined;
delete this.windowId;
delete this.messagePort;
delete this.receiverDevices;
delete this.defaultMediaType;
delete this.availableMediaTypes;
this.wasReceiverSelected = false;
}

View File

@@ -12,21 +12,18 @@ import { getMediaTypesForPageUrl } from "../../lib/utils";
import {
ReceiverSelection,
ReceiverSelectionActionType,
ReceiverSelectionCast,
ReceiverSelectionStop,
ReceiverSelectorMediaType
} from "./index";
import ReceiverSelector from "./ReceiverSelector";
async function createSelector() {
return new ReceiverSelector();
}
let sharedSelector: ReceiverSelector;
async function getSelector() {
if (!sharedSelector) {
try {
sharedSelector = await createSelector();
sharedSelector = new ReceiverSelector();
} catch (err) {
throw logger.error("Failed to create receiver selector.");
}
@@ -68,7 +65,7 @@ async function getSelection(
}
let defaultMediaType = ReceiverSelectorMediaType.Tab;
let availableMediaTypes;
let availableMediaTypes = ReceiverSelectorMediaType.None;
let pageUrl: string | undefined;
try {
@@ -84,10 +81,9 @@ async function getSelection(
logger.error(
"Failed to locate frame, falling back to default available media types."
);
availableMediaTypes = ReceiverSelectorMediaType.File;
}
// Enable app media type if initialized sender app is found
// Enable app media type if sender application is present
if (castInstance || selectionOpts?.withMediaSender) {
defaultMediaType = ReceiverSelectorMediaType.App;
availableMediaTypes |= ReceiverSelectorMediaType.App;
@@ -95,7 +91,7 @@ async function getSelection(
const opts = await options.getAll();
// Remove mirroring media types if mirroring is not enabled
// Disable mirroring media types if mirroring is not enabled
if (!opts.mirroringEnabled) {
availableMediaTypes &= ~(
ReceiverSelectorMediaType.Tab | ReceiverSelectorMediaType.Screen
@@ -113,7 +109,7 @@ async function getSelection(
}
// Get a new selector for each selection
sharedSelector = await createSelector();
sharedSelector = new ReceiverSelector();
function onReceiverChange() {
sharedSelector.update(receiverDevices.getDevices());
@@ -129,34 +125,52 @@ async function getSelection(
onReceiverChange
);
let onSelected: any;
let onCancelled: any;
let onError: any;
let onStop: any;
function onSelectorSelected(ev: CustomEvent<ReceiverSelectionCast>) {
logger.info("Selected receiver", ev.detail);
type EvParamsType = Parameters<
typeof sharedSelector.addEventListener
>[0];
removeListeners();
resolve({
actionType: ReceiverSelectionActionType.Cast,
receiverDevice: ev.detail.receiverDevice,
mediaType: ev.detail.mediaType,
filePath: ev.detail.filePath
});
}
function onSelectorStop(ev: CustomEvent<ReceiverSelectionStop>) {
logger.info("Stopping receiver app...", ev.detail);
function storeListener<T>(type: EvParamsType, fn: T) {
if (type === "selected") {
onSelected = fn;
} else if (type === "cancelled") {
onCancelled = fn;
} else if (type === "error") {
onError = fn;
} else if (type === "stop") {
onStop = fn;
}
receiverDevices.stopReceiverApp(ev.detail.receiverDevice.id);
return fn;
removeListeners();
resolve({
actionType: ReceiverSelectionActionType.Stop,
receiverDevice: ev.detail.receiverDevice
});
}
function onSelectorCancelled() {
logger.info("Cancelled receiver selection");
removeListeners();
resolve(null);
}
function onSelectorError(ev: CustomEvent<string>) {
removeListeners();
reject(ev.detail);
}
sharedSelector.addEventListener("selected", onSelectorSelected);
sharedSelector.addEventListener("stop", onSelectorStop);
sharedSelector.addEventListener("cancelled", onSelectorCancelled);
sharedSelector.addEventListener("error", onSelectorError);
function removeListeners() {
sharedSelector.removeEventListener("selected", onSelected);
sharedSelector.removeEventListener("cancelled", onCancelled);
sharedSelector.removeEventListener("error", onError);
sharedSelector.removeEventListener("stop", onStop);
sharedSelector.removeEventListener("selected", onSelectorSelected);
sharedSelector.removeEventListener("stop", onSelectorStop);
sharedSelector.removeEventListener(
"cancelled",
onSelectorCancelled
);
sharedSelector.removeEventListener("error", onSelectorError);
receiverDevices.removeEventListener(
"receiverDeviceUp",
@@ -172,70 +186,24 @@ async function getSelection(
);
}
sharedSelector.addEventListener(
"selected",
storeListener("selected", ev => {
logger.info("Selected receiver", ev.detail);
resolve({
actionType: ReceiverSelectionActionType.Cast,
receiverDevice: ev.detail.receiverDevice,
mediaType: ev.detail.mediaType,
filePath: ev.detail.filePath
});
removeListeners();
})
);
sharedSelector.addEventListener(
"cancelled",
storeListener("cancelled", () => {
logger.info("Cancelled receiver selection");
resolve(null);
removeListeners();
})
);
sharedSelector.addEventListener(
"error",
storeListener("error", ev => {
reject(ev.detail);
removeListeners();
})
);
sharedSelector.addEventListener(
"stop",
storeListener("stop", async ev => {
logger.info("Stopping receiver app...", ev.detail);
receiverDevices.stopReceiverApp(ev.detail.receiverDevice.id);
resolve({
actionType: ReceiverSelectionActionType.Stop,
receiverDevice: ev.detail.receiverDevice
});
removeListeners();
})
);
// Ensure status manager is initialized
await receiverDevices.init();
const pageInfo = pageUrl
? {
url: pageUrl,
tabId: contextTabId,
frameId: contextFrameId,
sessionRequest: selectionOpts?.sessionRequest
}
: undefined;
sharedSelector.open(
receiverDevices.getDevices(),
sharedSelector.open({
receiverDevices: receiverDevices.getDevices(),
defaultMediaType,
availableMediaTypes,
castInstance?.appId,
pageInfo
);
appId: castInstance?.appId,
// Create page info
pageInfo: pageUrl
? {
url: pageUrl,
tabId: contextTabId,
frameId: contextFrameId,
sessionRequest: selectionOpts?.sessionRequest
}
: undefined
});
});
}