Replace StatusManager

This commit is contained in:
hensm
2021-04-28 06:21:50 +01:00
parent c1172410f9
commit f44d142631
22 changed files with 295 additions and 286 deletions

View File

@@ -27,7 +27,7 @@
, "comma-style": [ "error", "first" ]
, "no-multiple-empty-lines": [ "error", { "max": 2 }]
, "no-console": [ "error", {
"allow": [ "info", "error" ]
"allow": [ "info", "warn", "error" ]
}]
, "operator-linebreak": [ "error", "after", {
"overrides": {

View File

@@ -4,7 +4,7 @@ import castv2 from "castv2";
import Session, { NS_CONNECTION, NS_RECEIVER } from "./Session";
import Media from "./Media";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
// Existing counterpart Media/Session objects

View File

@@ -2,10 +2,13 @@
import mdns from "mdns";
import StatusListener from "./chromecast/StatusListener";
import { ReceiverStatus } from "../types";
import { sendMessage } from "../lib/nativeMessaging";
import { ReceiverStatus } from "../types";
import { Message } from "../messaging";
import StatusListener from "./chromecast/StatusListener";
interface CastTxtRecord {
id: string; cd: string; rm: string;
@@ -34,12 +37,14 @@ function onBrowserServiceUp(service: mdns.Service) {
const txtRecord = service.txtRecord as CastTxtRecord;
sendMessage({
subject: "main:serviceUp"
subject: "main:receiverDeviceUp"
, data: {
host: service.addresses[0]
, port: service.port
, id: txtRecord.id
, friendlyName: txtRecord.fn
receiverDevice: {
host: service.addresses[0]
, port: service.port
, id: txtRecord.id
, friendlyName: txtRecord.fn
}
}
});
}
@@ -72,41 +77,20 @@ export function startDiscovery(options: InitializeOptions) {
return;
}
const { id } = service.txtRecord;
const listener = new StatusListener(
service.addresses[0]
, service.port);
service.addresses[0], service.port);
listener.on("receiverStatus", (status: ReceiverStatus) => {
const receiverStatusMessage: any = {
subject: "main:updateReceiverStatus"
sendMessage({
subject: "main:receiverDeviceUpdated"
, data: {
id
, status: {
volume: {
level: status.volume.level
, muted: status.volume.muted
}
}
receiverDeviceId: service.txtRecord.id
, status
}
};
if (status.applications && status.applications.length) {
const application = status.applications[0];
receiverStatusMessage.data.status.application = {
appId: application.appId
, displayName: application.displayName
, isIdleScreen: application.isIdleScreen
, statusText: application.statusText
};
}
sendMessage(receiverStatusMessage);
});
});
statusListeners.set(id, listener);
statusListeners.set(service.txtRecord.id, listener);
}
function onStatusBrowserServiceDown(_service: mdns.Service) {

View File

@@ -51,8 +51,8 @@ decodeTransform.on("data", (message: Message) => {
}
case "bridge:stopReceiverApp": {
const { host, port } = message.data.receiver;
stopReceiverApp(host, port);
const { receiverDevice } = message.data;
stopReceiverApp(receiverDevice.host, receiverDevice.port);
break;
}

View File

@@ -1,6 +1,6 @@
"use strict";
import { Receiver
import { ReceiverDevice
, ReceiverSelectionCast
, ReceiverSelectionStop
, ReceiverStatus } from "./types";
@@ -127,7 +127,7 @@ type MessageDefinitions = {
, "bridge:openReceiverSelector": string
, "bridge:closeReceiverSelector": {}
, "bridge:stopReceiverApp": { receiver: Receiver }
, "bridge:stopReceiverApp": { receiverDevice: ReceiverDevice }
, "bridge:startMediaServer": {
@@ -145,13 +145,21 @@ type MessageDefinitions = {
, "mediaCast:mediaServerError": {}
, "main:serviceUp": Receiver
, "main:serviceUp": ReceiverDevice
, "main:serviceDown": { id: string }
, "main:updateReceiverStatus": {
id: string
, status: ReceiverStatus
}
, "main:receiverDeviceUp": { receiverDevice: ReceiverDevice }
, "main:receiverDeviceDown": { receiverDeviceId: string }
, "main:receiverDeviceUpdated": {
receiverDeviceId: string
, status: ReceiverStatus
}
}

View File

@@ -14,8 +14,8 @@ export interface ReceiverStatus {
, transportId: string
, universalAppId: string
}>
, isActiveInput: boolean
, isStandBy: boolean
, isActiveInput?: boolean
, isStandBy?: boolean
, userEq: unknown
, volume: Volume
}
@@ -68,17 +68,17 @@ export enum ReceiverSelectionActionType {
export interface ReceiverSelectionCast {
actionType: ReceiverSelectionActionType.Cast;
receiver: Receiver;
receiver: ReceiverDevice;
mediaType: ReceiverSelectorMediaType;
filePath?: string;
}
export interface ReceiverSelectionStop {
actionType: ReceiverSelectionActionType.Stop;
receiver: Receiver;
receiver: ReceiverDevice;
}
export interface Receiver {
export interface ReceiverDevice {
host: string;
friendlyName: string;
id: string;

View File

@@ -12,7 +12,7 @@ import { ReceiverSelectionActionType
import ReceiverSelectorManager
from "./receiverSelector/ReceiverSelectorManager";
import StatusManager from "./StatusManager";
import receiverDevices from "./receiverDevices";
type AnyPort = Port | MessagePort;
@@ -37,20 +37,20 @@ export default new class ShimManager {
}
});
StatusManager.addEventListener("serviceUp", ev => {
receiverDevices.addEventListener("receiverDeviceUp", ev => {
for (const shim of this.activeShims) {
shim.contentPort.postMessage({
subject: "shim:serviceUp"
, data: { id: ev.detail.id }
, data: { id: ev.detail.receiverDevice.id }
});
}
});
StatusManager.addEventListener("serviceDown", ev => {
receiverDevices.addEventListener("receiverDeviceDown", ev => {
for (const shim of this.activeShims) {
shim.contentPort.postMessage({
subject: "shim:serviceDown"
, data: { id: ev.detail.id }
, data: { id: ev.detail.receiverDeviceId }
});
}
});
@@ -170,10 +170,10 @@ export default new class ShimManager {
case "main:shimReady": {
shim.appId = message.data.appId;
for (const receiver of StatusManager.getReceivers()) {
for (const receiverDevice of receiverDevices.getDevices()) {
shim.contentPort.postMessage({
subject: "shim:serviceUp"
, data: { id: receiver.id }
, data: { id: receiverDevice.id }
});
}

View File

@@ -1,163 +0,0 @@
"use strict";
import bridge from "../lib/bridge";
import logger from "../lib/logger";
import { Message, Port } from "../messaging";
import { TypedEventTarget } from "../lib/TypedEventTarget";
import { Receiver, ReceiverStatus } from "../types";
interface EventMap {
"serviceUp": Receiver;
"serviceDown": { id: string };
"statusUpdate": { id: string, status: ReceiverStatus };
}
export default new class StatusManager
extends TypedEventTarget<EventMap> {
private bridgePort: (Port | null) = null;
private receivers = new Map<string, Receiver>();
constructor() {
super();
// Bind listeners
this.onBridgePortMessage = this.onBridgePortMessage.bind(this);
this.onBridgePortDisconnect = this.onBridgePortDisconnect.bind(this);
}
public async init() {
if (!this.bridgePort) {
this.bridgePort = await this.createBridgePort();
}
}
public *getReceivers() {
for (const [ , receiver ] of this.receivers) {
if (receiver.status && receiver.status.application
&& receiver.status.volume) {
yield receiver;
}
}
}
public async stopReceiverApp(receiver: Receiver) {
if (!this.bridgePort) {
return;
}
this.bridgePort.postMessage({
subject: "bridge:stopReceiverApp"
, data: { receiver }
});
}
private async createBridgePort() {
const bridgePort = await bridge.connect();
bridgePort.onMessage.addListener(this.onBridgePortMessage);
bridgePort.onDisconnect.addListener(this.onBridgePortDisconnect);
bridgePort.postMessage({
subject: "bridge:initialize"
, data: {
shouldWatchStatus: true
}
});
return bridgePort;
}
/**
* Handles incoming bridge status messages, manages the
* receiver list, and dispatches events.
*/
private onBridgePortMessage(message: Message) {
switch (message.subject) {
case "main:serviceUp": {
const { data: receiver } = message;
this.receivers.set(receiver.id, receiver);
this.dispatchEvent(new CustomEvent("serviceUp", {
detail: receiver
}));
break;
}
case "main:serviceDown": {
const { data: { id }} = message;
if (this.receivers.has(id)) {
this.receivers.delete(id);
}
this.dispatchEvent(new CustomEvent("serviceDown", {
detail: { id }
}));
break;
}
case "main:updateReceiverStatus": {
const { data: { id, status }} = message;
const receiver = this.receivers.get(id);
if (!receiver) {
throw logger.error(`Could not find receiver (${id}) specified in status message.`);
}
// Merge with existing
this.receivers.set(id, {
...receiver
, status: {
...receiver.status
, ...status
}
});
this.dispatchEvent(new CustomEvent("statusUpdate", {
detail: { id, status }
}));
}
}
}
/**
* Runs once the status bridge has disconnected. Sends
* serviceDown messages for all receivers to all shims to
* update receiver availability, then clears the receiver
* list.
*
* Attempts to reinitialize the status bridge after 10
* seconds. If it fails immediately, this handler will be
* triggered again and the timer is reset for another 10
* seconds.
*/
private onBridgePortDisconnect() {
for (const [ , receiver ] of this.receivers) {
const serviceDownEvent = new CustomEvent("serviceDown", {
detail: { id: receiver.id }
});
this.dispatchEvent(serviceDownEvent);
}
// Cleanup
this.receivers.clear();
if (this.bridgePort) {
this.bridgePort.onDisconnect.removeListener(
this.onBridgePortDisconnect);
this.bridgePort.onMessage.removeListener(
this.onBridgePortMessage);
this.bridgePort = null;
}
window.setTimeout(async () => {
this.bridgePort = await this.createBridgePort();
}, 10000);
}
};

View File

@@ -11,7 +11,8 @@ import ReceiverSelectorManager
from "./receiverSelector/ReceiverSelectorManager";
import ShimManager from "./ShimManager";
import StatusManager from "./StatusManager";
import receiverDevices from "./receiverDevices";
import { initMenus } from "./menus";
import { initWhitelist } from "./whitelist";
@@ -155,7 +156,7 @@ async function init() {
await notifyBridgeCompat();
await StatusManager.init();
await receiverDevices.init();
await ShimManager.init();
await initMenus();

View File

@@ -0,0 +1,167 @@
"use strict";
import bridge from "../lib/bridge";
import logger from "../lib/logger";
import { TypedEventTarget } from "../lib/TypedEventTarget";
import messaging, { Message, Port } from "../messaging";
import { ReceiverDevice, ReceiverStatus } from "../types";
interface EventMap {
"receiverDeviceUp": { receiverDevice: ReceiverDevice }
, "receiverDeviceDown": { receiverDeviceId: string }
, "receiverDeviceUpdated": {
receiverDeviceId: string
, status: ReceiverStatus
}
}
export default new class extends TypedEventTarget<EventMap> {
/**
* Map of receiver device IDs to devices. Updated as
* receiverDevice messages are received from the bridge.
*/
private receiverDevices = new Map<string, ReceiverDevice>();
private bridgePort?: Port;
async init() {
if (!this.bridgePort) {
await this.refresh();
}
}
/**
* Initialize (or re-initialize) a bridge connection to
* start dispatching events.
*/
async refresh() {
this.bridgePort?.disconnect();
const port = await bridge.connect();
port.onMessage.addListener(this.onBridgeMessage);
port.onDisconnect.addListener(this.onBridgeDisconnect);
port.postMessage({
subject: "bridge:initialize"
, data: {
// Also send back status messages
shouldWatchStatus: true
}
});
this.bridgePort = port;
}
/**
* Get a list of receiver devices
*/
getDevices() {
return Array.from(this.receiverDevices.values());
}
/**
* Stops a receiver app running on a given device.
*/
stopReceiverApp(receiverDeviceId: string) {
if (!this.bridgePort) {
logger.error("Failed to stop receiver device, no bridge connection");
return;
}
const receiverDevice = this.receiverDevices.get(receiverDeviceId);
if (receiverDevice) {
this.bridgePort.postMessage({
subject: "bridge:stopReceiverApp"
, data: { receiverDevice }
});
}
}
private onBridgeMessage = (message: Message) => {
switch (message.subject) {
case "main:receiverDeviceUp": {
const { receiverDevice } = message.data;
this.receiverDevices.set(receiverDevice.id, receiverDevice);
this.dispatchEvent(
new CustomEvent("receiverDeviceUp"
, {
detail: { receiverDevice }
}));
break;
}
case "main:receiverDeviceDown": {
const { receiverDeviceId } = message.data;
if (this.receiverDevices.has(receiverDeviceId)) {
this.receiverDevices.delete(receiverDeviceId);
}
this.dispatchEvent(
new CustomEvent("receiverDeviceDown"
, {
detail: { receiverDeviceId }
}));
break;
}
case "main:receiverDeviceUpdated": {
const { receiverDeviceId, status } = message.data;
const receiverDevice =
this.receiverDevices.get(receiverDeviceId);
if (!receiverDevice) {
logger.error(`Receiver ID \`${receiverDeviceId}\` not found!`);
break;
}
if (receiverDevice.status) {
receiverDevice.status.isActiveInput = status.isActiveInput;
receiverDevice.status.isStandBy = status.isStandBy;
receiverDevice.status.userEq = status.userEq;
receiverDevice.status.volume = status.volume;
if (status.applications) {
receiverDevice.status.applications =
status.applications;
}
} else {
receiverDevice.status = status;
}
this.dispatchEvent(
new CustomEvent("receiverDeviceUpdated"
, {
detail: {
receiverDeviceId
, status: receiverDevice.status
}
}));
}
}
}
private onBridgeDisconnect = () => {
// Notify listeners of device availablility
for (const [ , receiverDevice ] of this.receiverDevices) {
const event = new CustomEvent("receiverDeviceDown", {
detail: { receiverDeviceId: receiverDevice.id }
});
this.dispatchEvent(event);
}
this.receiverDevices.clear();
// Re-initialize after 10 seconds
window.setTimeout(() => {
this.refresh();
}, 10000);
}
};

View File

@@ -8,7 +8,7 @@ import options from "../../lib/options";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { getWindowCenteredProps } from "../../lib/utils";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
import ReceiverSelector, {
ReceiverSelection
@@ -34,7 +34,7 @@ export default class NativeReceiverSelector extends ReceiverSelector {
}
public async open(
receivers: Receiver[]
receivers: ReceiverDevice[]
, defaultMediaType: ReceiverSelectorMediaType
, availableMediaTypes: ReceiverSelectorMediaType
, appId?: string): Promise<void> {

View File

@@ -9,7 +9,7 @@ import options from "../../lib/options";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
@@ -20,7 +20,7 @@ export default class PopupReceiverSelector extends ReceiverSelector {
private messagePort?: Port;
private messagePortDisconnected?: boolean;
private receivers?: Receiver[];
private receivers?: ReceiverDevice[];
private defaultMediaType?: ReceiverSelectorMediaType;
private availableMediaTypes?: ReceiverSelectorMediaType;
@@ -53,7 +53,7 @@ export default class PopupReceiverSelector extends ReceiverSelector {
}
public async open(
receivers: Receiver[]
receivers: ReceiverDevice[]
, defaultMediaType: ReceiverSelectorMediaType
, availableMediaTypes: ReceiverSelectorMediaType
, appId?: string): Promise<void> {
@@ -115,7 +115,7 @@ export default class PopupReceiverSelector extends ReceiverSelector {
}
}
public update(receivers: Receiver[]) {
public update(receivers: ReceiverDevice[]) {
this.receivers = receivers;
this.messagePort?.postMessage({
subject: "popup:update"

View File

@@ -1,7 +1,7 @@
"use strict";
import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
export enum ReceiverSelectorMediaType {
@@ -18,13 +18,13 @@ export enum ReceiverSelectionActionType {
export interface ReceiverSelectionCast {
actionType: ReceiverSelectionActionType.Cast;
receiver: Receiver;
receiver: ReceiverDevice;
mediaType: ReceiverSelectorMediaType;
filePath?: string;
}
export interface ReceiverSelectionStop {
actionType: ReceiverSelectionActionType.Stop;
receiver: Receiver;
receiver: ReceiverDevice;
}
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
@@ -43,12 +43,12 @@ export default abstract class ReceiverSelector
abstract readonly isOpen: boolean;
abstract open (
receivers: Receiver[]
receivers: ReceiverDevice[]
, defaultMediaType: ReceiverSelectorMediaType
, availableMediaTypes: ReceiverSelectorMediaType
, appId?: string): void;
abstract update (receivers: Receiver[]): void;
abstract update (receivers: ReceiverDevice[]): void;
abstract close (): void;
}

View File

@@ -4,7 +4,7 @@ import options from "../../lib/options";
import logger from "../../lib/logger";
import ShimManager from "../ShimManager";
import StatusManager from "../StatusManager";
import receiverDevices from "../receiverDevices";
import { getMediaTypesForPageUrl } from "../../lib/utils";
@@ -120,12 +120,15 @@ async function getSelection(
function onReceiverChange() {
sharedSelector.update(Array.from(StatusManager.getReceivers()));
sharedSelector.update(receiverDevices.getDevices());
}
StatusManager.addEventListener("serviceUp", onReceiverChange);
StatusManager.addEventListener("serviceDown", onReceiverChange);
StatusManager.addEventListener("statusUpdate", onReceiverChange);
receiverDevices.addEventListener(
"receiverDeviceUp", onReceiverChange);
receiverDevices.addEventListener(
"receiverDeviceDown", onReceiverChange);
receiverDevices.addEventListener(
"receiverDeviceUpdated", onReceiverChange);
let onSelected: any;
@@ -156,9 +159,12 @@ async function getSelection(
sharedSelector.removeEventListener("error", onError);
sharedSelector.removeEventListener("stop", onStop);
StatusManager.removeEventListener("serviceUp", onReceiverChange);
StatusManager.removeEventListener("serviceDown", onReceiverChange);
StatusManager.removeEventListener("statusUpdate", onReceiverChange);
receiverDevices.removeEventListener(
"receiverDeviceUp", onReceiverChange);
receiverDevices.removeEventListener(
"receiverDeviceDown", onReceiverChange);
receiverDevices.removeEventListener(
"receiverDeviceUpdated", onReceiverChange);
}
sharedSelector.addEventListener("selected"
@@ -191,10 +197,9 @@ async function getSelection(
sharedSelector.addEventListener("stop"
, storeListener("stop", async ev => {
logger.info("Stopped receiver app", ev.detail);
logger.info("Stopping receiver app...", ev.detail);
await StatusManager.init();
await StatusManager.stopReceiverApp(ev.detail.receiver);
receiverDevices.stopReceiverApp(ev.detail.receiver.id);
resolve({
actionType: ReceiverSelectionActionType.Stop
@@ -205,10 +210,10 @@ async function getSelection(
// Ensure status manager is initialized
await StatusManager.init();
await receiverDevices.init();
sharedSelector.open(
Array.from(StatusManager.getReceivers())
receiverDevices.getDevices()
, defaultMediaType
, availableMediaTypes
, currentShim?.appId);

View File

@@ -9,7 +9,7 @@ import { Message, Port } from "../messaging";
import nativeMessaging from "./nativeMessaging";
import options from "./options";
import { Receiver } from "../types";
import { ReceiverDevice } from "../types";
import { ReceiverSelectionCast
, ReceiverSelectionStop } from "../background/receiverSelector/ReceiverSelector";

View File

@@ -21,6 +21,14 @@ export class Logger {
console.info(formattedMessage);
}
}
public warn(message: string, data?: any) {
const formattedMessage = `${this.prefix} (Warning): ${message}`;
if (data) {
console.warn(formattedMessage, data);
} else {
console.warn(formattedMessage);
}
}
public error(message: string, data?: any) {
const formattedMessage = `${this.prefix} (Error): ${message}`;
if (data) {

View File

@@ -4,7 +4,7 @@ import Messenger from "./lib/Messenger";
import { TypedPort } from "./lib/TypedPort";
import { BridgeInfo } from "./lib/bridge";
import { Receiver, ReceiverStatus } from "./types";
import { ReceiverDevice, ReceiverStatus } from "./types";
import { ReceiverSelectorMediaType } from "./background/receiverSelector";
import { ReceiverSelection, ReceiverSelectionCast, ReceiverSelectionStop }
from "./background/receiverSelector/ReceiverSelector";
@@ -34,7 +34,7 @@ import { MediaInfo } from "./shim/cast/media";
type ExtMessageDefinitions = {
"popup:init": { appId?: string }
, "popup:update": {
receivers: Receiver[]
receivers: ReceiverDevice[]
, defaultMediaType?: ReceiverSelectorMediaType
, availableMediaTypes?: ReceiverSelectorMediaType
}
@@ -53,10 +53,10 @@ type ExtMessageDefinitions = {
, "main:sessionCreated": {}
, "shim:initialized": BridgeInfo
, "shim:serviceUp": { id: Receiver["id"] }
, "shim:serviceDown": { id: Receiver["id"] }
, "shim:serviceUp": { id: ReceiverDevice["id"] }
, "shim:serviceDown": { id: ReceiverDevice["id"] }
, "shim:launchApp": { receiver: Receiver }
, "shim:launchApp": { receiver: ReceiverDevice }
}
/**
@@ -184,7 +184,7 @@ type AppMessageDefinitions = {
, "bridge:openReceiverSelector": string
, "bridge:closeReceiverSelector": {}
, "bridge:stopReceiverApp": { receiver: Receiver }
, "bridge:stopReceiverApp": { receiverDevice: ReceiverDevice }
, "bridge:startMediaServer": {
@@ -202,11 +202,10 @@ type AppMessageDefinitions = {
, "mediaCast:mediaServerError": {}
, "main:serviceUp": Receiver
, "main:serviceDown": { id: string }
, "main:updateReceiverStatus": {
id: string
, "main:receiverDeviceUp": { receiverDevice: ReceiverDevice }
, "main:receiverDeviceDown": { receiverDeviceId: string }
, "main:receiverDeviceUpdated": {
receiverDeviceId: string
, status: ReceiverStatus
}
}

View File

@@ -5,7 +5,7 @@ import options from "../../lib/options";
import cast, { ensureInit } from "../../shim/export";
import { Message } from "../../messaging";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
function startMediaServer(filePath: string, port: number)
@@ -343,7 +343,7 @@ async function registerMediaElementListeners() {
interface InitOptions {
mediaUrl: string;
receiver: Receiver;
receiver: ReceiverDevice;
targetElementId?: number;
}

View File

@@ -4,14 +4,14 @@ import options from "../lib/options";
import cast, { ensureInit } from "../shim/export";
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
import { Receiver } from "../types";
import { ReceiverDevice } from "../types";
// Variables passed from background
const { selectedMedia
, selectedReceiver }
: { selectedMedia: ReceiverSelectorMediaType
, selectedReceiver: Receiver } = (window as any);
, selectedReceiver: ReceiverDevice } = (window as any);
const FX_CAST_RECEIVER_APP_NAMESPACE = "urn:x-cast:fx_cast";

View File

@@ -2,7 +2,7 @@
import logger from "../../lib/logger";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
import Session from "./Session";
@@ -177,7 +177,7 @@ export function requestSession(
}
export function _requestSession(
_receiver: Receiver
_receiver: ReceiverDevice
, successCallback?: RequestSessionSuccessCallback
, errorCallback?: ErrorCallback): void {
@@ -414,7 +414,7 @@ onMessage(async message => {
}
case "shim:launchApp": {
const receiver: Receiver = message.data.receiver;
const receiver: ReceiverDevice = message.data.receiver;
_requestSession(receiver
, session => {
apiConfig.sessionListener(session);

View File

@@ -3,7 +3,7 @@
import { Volume } from "./shim/cast/dataClasses";
export interface Receiver {
export interface ReceiverDevice {
host: string
friendlyName: string
, id: string
@@ -25,8 +25,8 @@ export interface ReceiverStatus {
, transportId: string
, universalAppId: string
}>
, isActiveInput: boolean
, isStandBy: boolean
, isActiveInput?: boolean
, isStandBy?: boolean
, userEq: unknown
, volume: Volume
}

View File

@@ -9,7 +9,7 @@ import options from "../../lib/options";
import messaging, { Message, Port } from "../../messaging";
import { getNextEllipsis } from "../../lib/utils";
import { Receiver } from "../../types";
import { ReceiverDevice } from "../../types";
import { ReceiverSelectionActionType
, ReceiverSelectorMediaType } from "../../background/receiverSelector";
@@ -31,7 +31,7 @@ browser.runtime.getPlatformInfo()
interface PopupAppProps {}
interface PopupAppState {
receivers: Receiver[];
receivers: ReceiverDevice[];
mediaType: ReceiverSelectorMediaType;
availableMediaTypes: ReceiverSelectorMediaType;
isLoading: boolean;
@@ -92,7 +92,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
if (availableMediaTypes && defaultMediaType) {
this.setState({
availableMediaTypes: availableMediaTypes
availableMediaTypes
, mediaType: defaultMediaType
});
}
@@ -214,7 +214,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
</>;
}
private onCast(receiver: Receiver) {
private onCast(receiver: ReceiverDevice) {
this.setState({
isLoading: true
});
@@ -230,7 +230,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
});
}
private onStop(receiver: Receiver) {
private onStop(receiver: ReceiverDevice) {
this.port?.postMessage({
subject: "receiverSelector:stop"
, data: {
@@ -274,11 +274,11 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
interface ReceiverEntryProps {
receiver: Receiver;
receiver: ReceiverDevice;
isLoading: boolean;
canCast: boolean;
onCast (receiver: Receiver): void;
onStop (receiver: Receiver): void;
onCast (receiver: ReceiverDevice): void;
onStop (receiver: ReceiverDevice): void;
}
interface ReceiverEntryState {
@@ -320,28 +320,27 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
}
public render() {
if (!this.props.receiver.status) {
const { status } = this.props.receiver;
if (!status) {
return;
}
const { application } = this.props.receiver.status;
const application = status.applications?.[0];
return (
<li className="receiver">
<div className="receiver__name">
{ this.props.receiver.friendlyName }
</div>
<div className="receiver__address"
title={ !application.isIdleScreen ? application.statusText : "" }>
{ application.isIdleScreen
? `${this.props.receiver.host}:${this.props.receiver.port}`
: application.statusText }
<div className="receiver__address">
{ application && !application.isIdleScreen
? application.statusText
: `${this.props.receiver.host}:${this.props.receiver.port}` }
</div>
<button className="button receiver__connect"
onClick={ this.handleCast }
disabled={ this.state.showAlternateAction
? application.isIdleScreen
: (this.props.isLoading || !this.props.canCast) }>
disabled={ (application && application.isIdleScreen)
?? (this.props.isLoading || !this.props.canCast) }>
{ this.state.isLoading
? _("popupCastingButtonTitle"
, (this.state.isLoading
@@ -356,13 +355,14 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
}
private handleCast() {
if (!this.props.receiver.status) {
const { status } = this.props.receiver;
if (!status) {
return;
}
const { application } = this.props.receiver.status;
const application = status.applications?.[0];
if (!application.isIdleScreen && this.state.showAlternateAction) {
if (!application?.isIdleScreen && this.state.showAlternateAction) {
this.props.onStop(this.props.receiver);
} else {
this.props.onCast(this.props.receiver);