Add typed messaging

This commit is contained in:
hensm
2020-02-18 07:37:20 +00:00
parent 652fd21f77
commit 2eeaff4c15
19 changed files with 431 additions and 227 deletions

View File

@@ -3,11 +3,12 @@
import bridge from "../../lib/bridge";
import knownApps from "../../lib/knownApps";
import logger from "../../lib/logger";
import { Message, Port } from "../../lib/messaging";
import options from "../../lib/options";
import { TypedEventTarget } from "../../lib/typedEvents";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { getWindowCenteredProps } from "../../lib/utils";
import { Message, Receiver } from "../../types";
import { Receiver } from "../../types";
import ReceiverSelector, {
ReceiverSelection
@@ -17,27 +18,20 @@ import ReceiverSelector, {
const _ = browser.i18n.getMessage;
interface NativeReceiverSelectorSelectedMessage extends Message {
subject: "main:/receiverSelector/selected";
data: ReceiverSelection;
}
interface NativeReceiverSelectorErrorMessage extends Message {
subject: "main:/receiverSelector/error";
data: string;
}
// TODO: Figure out lifetime properly
export default class NativeReceiverSelector
extends TypedEventTarget<ReceiverSelectorEvents>
implements ReceiverSelector {
private bridgePort: (browser.runtime.Port | null) = null;
private bridgePort: (Port | null) = null;
private wasReceiverSelected: boolean = false;
private _isOpen: boolean = false;
constructor () {
super();
this.onBridgePortMessage = this.onBridgePortMessage.bind(this);
}
get isOpen () {
return this._isOpen;
}
@@ -50,31 +44,7 @@ export default class NativeReceiverSelector
this.bridgePort = await bridge.connect();
this.bridgePort.onMessage.addListener((message: Message) => {
switch (message.subject) {
case "main:/receiverSelector/selected": {
this.onBridgePortMessageSelected(
message as NativeReceiverSelectorSelectedMessage);
break;
}
case "main:/receiverSelector/error": {
this.onBridgePortMessageError(
message as NativeReceiverSelectorErrorMessage);
break;
}
case "main:/receiverSelector/close": {
this.onBridgePortMessageClose();
break;
}
case "main:/receiverSelector/stop": {
this.dispatchEvent(new CustomEvent("stop", {
detail: message.data
}));
break;
}
}
});
this.bridgePort.onMessage.addListener(this.onBridgePortMessage);
this.bridgePort.onDisconnect.addListener(() => {
this.bridgePort = null;
this.wasReceiverSelected = false;
@@ -128,40 +98,46 @@ export default class NativeReceiverSelector
this._isOpen = false;
}
private async onBridgePortMessage (message: Message) {
switch (message.subject) {
case "main:/receiverSelector/selected": {
this.wasReceiverSelected = true;
this.dispatchEvent(new CustomEvent("selected", {
detail: message.data
}));
private async onBridgePortMessageSelected (
message: NativeReceiverSelectorSelectedMessage) {
if (!(await options.get("receiverSelectorWaitForConnection"))) {
this.close();
}
this.wasReceiverSelected = true;
break;
}
case "main:/receiverSelector/error": {
logger.error("Native receiver selector error", message.data);
this.dispatchEvent(new CustomEvent("error"));
break;
}
case "main:/receiverSelector/close": {
if (!this.wasReceiverSelected) {
this.dispatchEvent(new CustomEvent("cancelled"));
}
this.dispatchEvent(new CustomEvent("selected", {
detail: message.data
}));
if (this.bridgePort) {
this.bridgePort.disconnect();
}
if (!(await options.get("receiverSelectorWaitForConnection"))) {
this.close();
this.bridgePort = null;
this.wasReceiverSelected = false;
this._isOpen = false;
break;
}
case "main:/receiverSelector/stop": {
this.dispatchEvent(new CustomEvent("stop", {
detail: message.data
}));
break;
}
}
}
private async onBridgePortMessageError (
message: NativeReceiverSelectorErrorMessage) {
logger.error("Native receiver selector error", message.data);
this.dispatchEvent(new CustomEvent("error"));
}
private async onBridgePortMessageClose () {
if (!this.wasReceiverSelected) {
this.dispatchEvent(new CustomEvent("cancelled"));
}
if (this.bridgePort) {
this.bridgePort.disconnect();
}
this.bridgePort = null;
this.wasReceiverSelected = false;
this._isOpen = false;
}
}

View File

@@ -5,11 +5,12 @@ import ReceiverSelector, {
, ReceiverSelectorMediaType } from "./ReceiverSelector";
import logger from "../../lib/logger";
import messaging, { Port, Message } from "../../lib/messaging";
import options from "../../lib/options";
import { TypedEventTarget } from "../../lib/typedEvents";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
import { Message, Receiver } from "../../types";
import { Receiver } from "../../types";
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
@@ -20,7 +21,7 @@ export default class PopupReceiverSelector
private windowId?: number;
private messagePort?: browser.runtime.Port;
private messagePort?: Port;
private messagePortDisconnected?: boolean;
private receivers?: Receiver[];
@@ -37,6 +38,7 @@ export default class PopupReceiverSelector
super();
// Bind methods to pass to addListener
this.onConnect = this.onConnect.bind(this);
this.onPopupMessage = this.onPopupMessage.bind(this);
this.onWindowsRemoved = this.onWindowsRemoved.bind(this);
this.onWindowsFocusChanged = this.onWindowsFocusChanged.bind(this);
@@ -47,39 +49,7 @@ export default class PopupReceiverSelector
* Handle incoming message channel connection from popup
* window script.
*/
browser.runtime.onConnect.addListener(port => {
// Don't pollute history
browser.history.deleteUrl({ url: POPUP_URL });
if (port.name !== "popup") {
return;
}
// Disconnect existing port
if (this.messagePort) {
this.messagePort.disconnect();
}
this.messagePort = port;
this.messagePort.onMessage.addListener(this.onPopupMessage);
this.messagePort.onDisconnect.addListener(() => {
this.messagePortDisconnected = true;
});
this.messagePort.postMessage({
subject: "popup:/sendRequestedAppId"
, data: { requestedAppId: this.requestedAppId }
});
this.messagePort.postMessage({
subject: "popup:/populateReceiverList"
, data: {
receivers: this.receivers
, defaultMediaType: this.defaultMediaType
, availableMediaTypes: this.availableMediaTypes
}
});
});
messaging.onConnect.addListener(this.onConnect);
}
get isOpen () {
@@ -162,6 +132,46 @@ export default class PopupReceiverSelector
}
}
private onConnect (port: Port) {
browser.history.deleteUrl({ url: POPUP_URL });
if (port.name !== "popup") {
return;
}
if (this.messagePort) {
this.messagePort.disconnect();
}
this.messagePort = port;
this.messagePort.onMessage.addListener(this.onPopupMessage);
this.messagePort.onDisconnect.addListener(() => {
this.messagePortDisconnected = true;
});
if (!this.requestedAppId
|| !this.receivers
|| !this.defaultMediaType
|| !this.availableMediaTypes) {
throw logger.error("Popup receiver data not found.");
}
this.messagePort.postMessage({
subject: "popup:/sendRequestedAppId"
, data: { requestedAppId: this.requestedAppId }
});
this.messagePort.postMessage({
subject: "popup:/populateReceiverList"
, data: {
receivers: this.receivers
, defaultMediaType: this.defaultMediaType
, availableMediaTypes: this.availableMediaTypes
}
});
messaging.onConnect.removeListener(this.onConnect);
}
/**
* Handles popup messages.

View File

@@ -1,6 +1,6 @@
"use strict";
import { TypedEventTarget } from "../../lib/typedEvents";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { Receiver } from "../../types";