mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Enable strict mode for extension build
This commit is contained in:
@@ -4,7 +4,4 @@
|
|||||||
"./src/**/*"
|
"./src/**/*"
|
||||||
, "./@types/**/*"
|
, "./@types/**/*"
|
||||||
]
|
]
|
||||||
, "compilerOptions": {
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import bridge from "../lib/bridge";
|
import bridge from "../lib/bridge";
|
||||||
import loadSender from "../lib/loadSender";
|
import loadSender from "../lib/loadSender";
|
||||||
|
import logger from "../lib/logger";
|
||||||
import options from "../lib/options";
|
import options from "../lib/options";
|
||||||
|
|
||||||
import { Message } from "../types";
|
import { Message } from "../types";
|
||||||
@@ -87,6 +88,11 @@ export default new class ShimManager {
|
|||||||
private async createShimFromContent (
|
private async createShimFromContent (
|
||||||
contentPort: browser.runtime.Port): Promise<Shim> {
|
contentPort: browser.runtime.Port): Promise<Shim> {
|
||||||
|
|
||||||
|
if (contentPort.sender?.tab?.id === undefined
|
||||||
|
|| contentPort.sender?.frameId === undefined) {
|
||||||
|
throw logger.error("Content shim created with an invalid port context.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there's already an active shim for the sender
|
* If there's already an active shim for the sender
|
||||||
* tab/frame ID, disconnect it.
|
* tab/frame ID, disconnect it.
|
||||||
@@ -135,6 +141,11 @@ export default new class ShimManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleContentMessage (shim: Shim, message: Message) {
|
private async handleContentMessage (shim: Shim, message: Message) {
|
||||||
|
if (shim.contentTabId === undefined
|
||||||
|
|| shim.contentFrameId === undefined) {
|
||||||
|
throw logger.error("Shim associated with content sender missing tab/frame ID");
|
||||||
|
}
|
||||||
|
|
||||||
const [ destination ] = message.subject.split(":/");
|
const [ destination ] = message.subject.split(":/");
|
||||||
if (destination === "bridge") {
|
if (destination === "bridge") {
|
||||||
shim.bridgePort.postMessage(message);
|
shim.bridgePort.postMessage(message);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import bridge from "../lib/bridge";
|
import bridge from "../lib/bridge";
|
||||||
|
import logger from "../lib/logger";
|
||||||
|
|
||||||
import { TypedEventTarget } from "../lib/typedEvents";
|
import { TypedEventTarget } from "../lib/typedEvents";
|
||||||
import { Message, Receiver, ReceiverStatus } from "../types";
|
import { Message, Receiver, ReceiverStatus } from "../types";
|
||||||
@@ -38,7 +39,7 @@ interface EventMap {
|
|||||||
export default new class StatusManager
|
export default new class StatusManager
|
||||||
extends TypedEventTarget<EventMap> {
|
extends TypedEventTarget<EventMap> {
|
||||||
|
|
||||||
private bridgePort: browser.runtime.Port;
|
private bridgePort: (browser.runtime.Port | null) = null;
|
||||||
private receivers = new Map<string, Receiver>();
|
private receivers = new Map<string, Receiver>();
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -131,6 +132,10 @@ export default new class StatusManager
|
|||||||
|
|
||||||
const receiver = this.receivers.get(id);
|
const receiver = this.receivers.get(id);
|
||||||
|
|
||||||
|
if (!receiver) {
|
||||||
|
throw logger.error(`Could not find receiver (${id}) specified in status message.`);
|
||||||
|
}
|
||||||
|
|
||||||
// Merge with existing
|
// Merge with existing
|
||||||
this.receivers.set(id, {
|
this.receivers.set(id, {
|
||||||
...receiver
|
...receiver
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import defaultOptions from "../defaultOptions";
|
import defaultOptions from "../defaultOptions";
|
||||||
import loadSender from "../lib/loadSender";
|
import loadSender from "../lib/loadSender";
|
||||||
|
import logger from "../lib/logger";
|
||||||
import options from "../lib/options";
|
import options from "../lib/options";
|
||||||
|
|
||||||
import { getChromeUserAgent } from "../lib/userAgents";
|
import { getChromeUserAgent } from "../lib/userAgents";
|
||||||
@@ -67,6 +68,10 @@ function initBrowserAction () {
|
|||||||
* top-level frame.
|
* top-level frame.
|
||||||
*/
|
*/
|
||||||
browser.browserAction.onClicked.addListener(async tab => {
|
browser.browserAction.onClicked.addListener(async tab => {
|
||||||
|
if (tab.id === undefined) {
|
||||||
|
throw logger.error("Tab ID not found in browser action handler.");
|
||||||
|
}
|
||||||
|
|
||||||
const selection = await ReceiverSelectorManager.getSelection(tab.id);
|
const selection = await ReceiverSelectorManager.getSelection(tab.id);
|
||||||
|
|
||||||
if (selection) {
|
if (selection) {
|
||||||
@@ -140,6 +145,10 @@ async function initMenus () {
|
|||||||
browser.menus.onClicked.addListener(async (info, tab) => {
|
browser.menus.onClicked.addListener(async (info, tab) => {
|
||||||
if (info.parentMenuItemId === menuIdWhitelist) {
|
if (info.parentMenuItemId === menuIdWhitelist) {
|
||||||
const pattern = whitelistChildMenuPatterns.get(info.menuItemId);
|
const pattern = whitelistChildMenuPatterns.get(info.menuItemId);
|
||||||
|
if (!pattern) {
|
||||||
|
throw logger.error(`Whitelist pattern not found for menu item ID ${info.menuItemId}.`);
|
||||||
|
}
|
||||||
|
|
||||||
const whitelist = await options.get("userAgentWhitelist");
|
const whitelist = await options.get("userAgentWhitelist");
|
||||||
|
|
||||||
// Add to whitelist and update options
|
// Add to whitelist and update options
|
||||||
@@ -150,6 +159,17 @@ async function initMenus () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (tab?.id === undefined) {
|
||||||
|
throw logger.error("Menu handler tab ID not found.");
|
||||||
|
}
|
||||||
|
if (info.frameId === undefined) {
|
||||||
|
throw logger.error("Menu handler frame ID not found.");
|
||||||
|
}
|
||||||
|
if (!info.pageUrl) {
|
||||||
|
throw logger.error("Menu handler page URL not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const availableMediaTypes = getMediaTypesForPageUrl(info.pageUrl);
|
const availableMediaTypes = getMediaTypesForPageUrl(info.pageUrl);
|
||||||
|
|
||||||
switch (info.menuItemId) {
|
switch (info.menuItemId) {
|
||||||
@@ -157,6 +177,11 @@ async function initMenus () {
|
|||||||
const selection = await ReceiverSelectorManager.getSelection(
|
const selection = await ReceiverSelectorManager.getSelection(
|
||||||
tab.id, info.frameId);
|
tab.id, info.frameId);
|
||||||
|
|
||||||
|
// Selection cancelled
|
||||||
|
if (!selection) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
loadSender({
|
loadSender({
|
||||||
tabId: tab.id
|
tabId: tab.id
|
||||||
, frameId: info.frameId
|
, frameId: info.frameId
|
||||||
@@ -447,6 +472,10 @@ function initWhitelist () {
|
|||||||
const { os } = await browser.runtime.getPlatformInfo();
|
const { os } = await browser.runtime.getPlatformInfo();
|
||||||
const chromeUserAgent = getChromeUserAgent(os);
|
const chromeUserAgent = getChromeUserAgent(os);
|
||||||
|
|
||||||
|
if (!details.requestHeaders) {
|
||||||
|
throw logger.error("OnBeforeSendHeaders handler details missing requestHeaders.");
|
||||||
|
}
|
||||||
|
|
||||||
const host = details.requestHeaders.find(
|
const host = details.requestHeaders.find(
|
||||||
header => header.name === "Host");
|
header => header.name === "Host");
|
||||||
|
|
||||||
@@ -457,7 +486,7 @@ function initWhitelist () {
|
|||||||
* so pretend to be an old version of Chrome to get the old
|
* so pretend to be an old version of Chrome to get the old
|
||||||
* site.
|
* site.
|
||||||
*/
|
*/
|
||||||
if (host.value === "www.youtube.com") {
|
if (host?.value === "www.youtube.com") {
|
||||||
header.value = getChromeUserAgent(os, true);
|
header.value = getChromeUserAgent(os, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default class NativeReceiverSelector
|
|||||||
extends TypedEventTarget<ReceiverSelectorEvents>
|
extends TypedEventTarget<ReceiverSelectorEvents>
|
||||||
implements ReceiverSelector {
|
implements ReceiverSelector {
|
||||||
|
|
||||||
private bridgePort: browser.runtime.Port;
|
private bridgePort: (browser.runtime.Port | null) = null;
|
||||||
private wasReceiverSelected: boolean = false;
|
private wasReceiverSelected: boolean = false;
|
||||||
private _isOpen: boolean = false;
|
private _isOpen: boolean = false;
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import ReceiverSelector, {
|
|||||||
ReceiverSelectorEvents
|
ReceiverSelectorEvents
|
||||||
, ReceiverSelectorMediaType } from "./ReceiverSelector";
|
, ReceiverSelectorMediaType } from "./ReceiverSelector";
|
||||||
|
|
||||||
|
import logger from "../../lib/logger";
|
||||||
import options from "../../lib/options";
|
import options from "../../lib/options";
|
||||||
|
|
||||||
import { TypedEventTarget } from "../../lib/typedEvents";
|
import { TypedEventTarget } from "../../lib/typedEvents";
|
||||||
import { getWindowCenteredProps } from "../../lib/utils";
|
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
|
||||||
import { Message, Receiver } from "../../types";
|
import { Message, Receiver } from "../../types";
|
||||||
|
|
||||||
|
|
||||||
@@ -15,19 +16,19 @@ export default class PopupReceiverSelector
|
|||||||
extends TypedEventTarget<ReceiverSelectorEvents>
|
extends TypedEventTarget<ReceiverSelectorEvents>
|
||||||
implements ReceiverSelector {
|
implements ReceiverSelector {
|
||||||
|
|
||||||
private windowId: number;
|
private windowId?: number;
|
||||||
|
|
||||||
private messagePort: browser.runtime.Port;
|
private messagePort?: browser.runtime.Port;
|
||||||
private messagePortDisconnected: boolean;
|
private messagePortDisconnected?: boolean;
|
||||||
|
|
||||||
private receivers: Receiver[];
|
private receivers?: Receiver[];
|
||||||
private defaultMediaType: ReceiverSelectorMediaType;
|
private defaultMediaType?: ReceiverSelectorMediaType;
|
||||||
private availableMediaTypes: ReceiverSelectorMediaType;
|
private availableMediaTypes?: ReceiverSelectorMediaType;
|
||||||
|
|
||||||
private wasReceiverSelected: boolean = false;
|
private wasReceiverSelected: boolean = false;
|
||||||
|
|
||||||
private _isOpen: boolean = false;
|
private _isOpen: boolean = false;
|
||||||
private requestedAppId: string;
|
private requestedAppId?: string;
|
||||||
|
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -97,9 +98,22 @@ export default class PopupReceiverSelector
|
|||||||
this.defaultMediaType = defaultMediaType;
|
this.defaultMediaType = defaultMediaType;
|
||||||
this.availableMediaTypes = availableMediaTypes;
|
this.availableMediaTypes = availableMediaTypes;
|
||||||
|
|
||||||
// Calculate centered size/position based on current window
|
|
||||||
const centeredProps = getWindowCenteredProps(
|
let centeredProps: WindowCenteredProps = {
|
||||||
await browser.windows.getCurrent(), 350, 200);
|
left: 100
|
||||||
|
, top: 100
|
||||||
|
, width: 350
|
||||||
|
, height: 200
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Calculate centered size/position based on current window
|
||||||
|
centeredProps = getWindowCenteredProps(
|
||||||
|
await browser.windows.getCurrent()
|
||||||
|
, centeredProps.width, centeredProps.height);
|
||||||
|
} catch {
|
||||||
|
// Shouldn't ever hit this, but defaults are provided in case
|
||||||
|
}
|
||||||
|
|
||||||
const popup = await browser.windows.create({
|
const popup = await browser.windows.create({
|
||||||
url: "ui/popup/index.html"
|
url: "ui/popup/index.html"
|
||||||
@@ -107,6 +121,10 @@ export default class PopupReceiverSelector
|
|||||||
, ...centeredProps
|
, ...centeredProps
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (popup?.id === undefined) {
|
||||||
|
throw logger.error("Failed to create receiver selector popup.");
|
||||||
|
}
|
||||||
|
|
||||||
this._isOpen = true;
|
this._isOpen = true;
|
||||||
this.windowId = popup.id;
|
this.windowId = popup.id;
|
||||||
|
|
||||||
@@ -132,7 +150,7 @@ export default class PopupReceiverSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._isOpen = false;
|
this._isOpen = false;
|
||||||
this.requestedAppId = null;
|
this.requestedAppId = undefined;
|
||||||
|
|
||||||
if (this.messagePort && !this.messagePortDisconnected) {
|
if (this.messagePort && !this.messagePortDisconnected) {
|
||||||
this.messagePort.disconnect();
|
this.messagePort.disconnect();
|
||||||
@@ -183,10 +201,11 @@ export default class PopupReceiverSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
this.windowId = null;
|
this.windowId = undefined;
|
||||||
this.messagePort = null;
|
this.messagePort = undefined;
|
||||||
this.receivers = null;
|
this.receivers = undefined;
|
||||||
this.defaultMediaType = null;
|
this.defaultMediaType = undefined;
|
||||||
|
this.availableMediaTypes = undefined;
|
||||||
this.wasReceiverSelected = false;
|
this.wasReceiverSelected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +222,9 @@ export default class PopupReceiverSelector
|
|||||||
browser.windows.onFocusChanged.removeListener(
|
browser.windows.onFocusChanged.removeListener(
|
||||||
this.onWindowsFocusChanged);
|
this.onWindowsFocusChanged);
|
||||||
|
|
||||||
browser.windows.remove(this.windowId);
|
if (this.windowId) {
|
||||||
|
browser.windows.remove(this.windowId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import options from "../../lib/options";
|
import options from "../../lib/options";
|
||||||
|
import logger from "../../lib/logger";
|
||||||
|
|
||||||
import ShimManager from "../ShimManager";
|
import ShimManager from "../ShimManager";
|
||||||
import StatusManager from "../StatusManager";
|
import StatusManager from "../StatusManager";
|
||||||
@@ -38,7 +39,11 @@ let sharedSelector: ReceiverSelector;
|
|||||||
|
|
||||||
async function getSelector () {
|
async function getSelector () {
|
||||||
if (!sharedSelector) {
|
if (!sharedSelector) {
|
||||||
sharedSelector = await createSelector();
|
try {
|
||||||
|
sharedSelector = await createSelector();
|
||||||
|
} catch (err) {
|
||||||
|
throw logger.error("Failed to create receiver selector.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedSelector;
|
return sharedSelector;
|
||||||
@@ -59,7 +64,7 @@ async function getSelection (
|
|||||||
contextTabId: number
|
contextTabId: number
|
||||||
, contextFrameId = 0
|
, contextFrameId = 0
|
||||||
, withMediaSender = false)
|
, withMediaSender = false)
|
||||||
: Promise<ReceiverSelection> {
|
: Promise<ReceiverSelection | null> {
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let currentShim = ShimManager.getShim(
|
let currentShim = ShimManager.getShim(
|
||||||
@@ -71,7 +76,7 @@ async function getSelection (
|
|||||||
*/
|
*/
|
||||||
if (currentShim?.requestedAppId ===
|
if (currentShim?.requestedAppId ===
|
||||||
await options.get("mirroringAppId")) {
|
await options.get("mirroringAppId")) {
|
||||||
currentShim = null;
|
currentShim = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let defaultMediaType = ReceiverSelectorMediaType.Tab;
|
let defaultMediaType = ReceiverSelectorMediaType.Tab;
|
||||||
@@ -164,8 +169,7 @@ async function getSelection (
|
|||||||
Array.from(StatusManager.getReceivers())
|
Array.from(StatusManager.getReceivers())
|
||||||
, defaultMediaType
|
, defaultMediaType
|
||||||
, availableMediaTypes
|
, availableMediaTypes
|
||||||
, currentShim?.requestedAppId
|
, currentShim?.requestedAppId ?? DEFAULT_MEDIA_RECEIVER_APP_ID);
|
||||||
?? (withMediaSender && DEFAULT_MEDIA_RECEIVER_APP_ID));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
|
||||||
|
import logger from "./logger";
|
||||||
import nativeMessaging from "./nativeMessaging";
|
import nativeMessaging from "./nativeMessaging";
|
||||||
import options from "./options";
|
import options from "./options";
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ async function connect (): Promise<browser.runtime.Port> {
|
|||||||
bridgePort.onDisconnect.addListener(() => {
|
bridgePort.onDisconnect.addListener(() => {
|
||||||
if (bridgePort.error) {
|
if (bridgePort.error) {
|
||||||
console.error(`${applicationName} disconnected:`
|
console.error(`${applicationName} disconnected:`
|
||||||
, this.bridgePort.error.message);
|
, bridgePort.error.message);
|
||||||
} else {
|
} else {
|
||||||
console.info(`${applicationName} disconnected`);
|
console.info(`${applicationName} disconnected`);
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,10 @@ export interface BridgeInfo {
|
|||||||
|
|
||||||
async function getInfo (): Promise<BridgeInfo> {
|
async function getInfo (): Promise<BridgeInfo> {
|
||||||
const applicationName = await options.get("bridgeApplicationName");
|
const applicationName = await options.get("bridgeApplicationName");
|
||||||
|
if (!applicationName) {
|
||||||
|
throw logger.error("Bridge application name not found.");
|
||||||
|
}
|
||||||
|
|
||||||
let applicationVersion: string;
|
let applicationVersion: string;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -45,7 +50,7 @@ async function getInfo (): Promise<BridgeInfo> {
|
|||||||
, { subject: "bridge:/getInfo"
|
, { subject: "bridge:/getInfo"
|
||||||
, data: version });
|
, data: version });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return null;
|
throw logger.error("Failed to connect to bridge application");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import logger from "./logger";
|
||||||
import { stringify } from "./utils";
|
import { stringify } from "./utils";
|
||||||
|
|
||||||
import { ReceiverSelection
|
import { ReceiverSelection
|
||||||
@@ -27,6 +28,11 @@ export default async function loadSender (opts: LoadSenderOptions) {
|
|||||||
switch (opts.selection.mediaType) {
|
switch (opts.selection.mediaType) {
|
||||||
case ReceiverSelectorMediaType.App: {
|
case ReceiverSelectorMediaType.App: {
|
||||||
const shim = ShimManager.getShim(opts.tabId, opts.frameId);
|
const shim = ShimManager.getShim(opts.tabId, opts.frameId);
|
||||||
|
if (!shim) {
|
||||||
|
throw logger.error(`Shim not found at tabId ${
|
||||||
|
opts.tabId} / frameId ${opts.frameId}`)
|
||||||
|
}
|
||||||
|
|
||||||
shim.contentPort.postMessage({
|
shim.contentPort.postMessage({
|
||||||
subject: "shim:/launchApp"
|
subject: "shim:/launchApp"
|
||||||
, data: { receiver: opts.selection.receiver }
|
, data: { receiver: opts.selection.receiver }
|
||||||
|
|||||||
19
ext/src/lib/logger.ts
Normal file
19
ext/src/lib/logger.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
export class Logger {
|
||||||
|
constructor (private prefix: string) {}
|
||||||
|
|
||||||
|
log (message: string) {
|
||||||
|
console.log(`${this.prefix} (Log): ${message}`);
|
||||||
|
}
|
||||||
|
debug (message: string) {
|
||||||
|
console.debug(`${this.prefix} (Debug): ${message}`);
|
||||||
|
}
|
||||||
|
error (message: string) {
|
||||||
|
const formattedMessage = `${this.prefix} (Error): ${message}`;
|
||||||
|
console.error(formattedMessage);
|
||||||
|
return new Error(formattedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Logger("fx_cast");
|
||||||
@@ -116,7 +116,7 @@ function connectNative (application: string) {
|
|||||||
|
|
||||||
socket.addEventListener("close", ev => {
|
socket.addEventListener("close", ev => {
|
||||||
if (ev.code !== 1000) {
|
if (ev.code !== 1000) {
|
||||||
this.error = {
|
portObject.error = {
|
||||||
// TODO: Set a proper error message
|
// TODO: Set a proper error message
|
||||||
message: ""
|
message: ""
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import defaultOptions from "../defaultOptions";
|
import defaultOptions from "../defaultOptions";
|
||||||
|
|
||||||
|
import logger from "./logger";
|
||||||
|
|
||||||
import { ReceiverSelectorType } from "../background/receiverSelector";
|
import { ReceiverSelectorType } from "../background/receiverSelector";
|
||||||
import { TypedEventTarget } from "./typedEvents";
|
import { TypedEventTarget } from "./typedEvents";
|
||||||
import { TypedStorageArea } from "./typedStorage";
|
import { TypedStorageArea } from "./typedStorage";
|
||||||
@@ -116,6 +118,8 @@ export default new class extends TypedEventTarget<EventMap> {
|
|||||||
|
|
||||||
if (options.hasOwnProperty(name)) {
|
if (options.hasOwnProperty(name)) {
|
||||||
return options[name];
|
return options[name];
|
||||||
|
} else {
|
||||||
|
throw logger.error(`Failed to find option ${name} in storage.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,20 +143,16 @@ export default new class extends TypedEventTarget<EventMap> {
|
|||||||
* storage are set. Does not override any existing options.
|
* storage are set. Does not override any existing options.
|
||||||
*/
|
*/
|
||||||
public async update (defaults = defaultOptions): Promise<void> {
|
public async update (defaults = defaultOptions): Promise<void> {
|
||||||
const oldOpts = await this.getAll();
|
const newOpts = await this.getAll();
|
||||||
const newOpts: Partial<Options> = {};
|
|
||||||
|
|
||||||
// Find options not already in storage
|
// Find options not already in storage
|
||||||
for (const [ optName, optVal ] of Object.entries(defaults)) {
|
for (const [ optName, optVal ] of Object.entries(defaults)) {
|
||||||
if (!oldOpts.hasOwnProperty(optName)) {
|
if (!newOpts.hasOwnProperty(optName)) {
|
||||||
newOpts[optName] = optVal;
|
newOpts[optName] = optVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update storage with default values of new options
|
// Update storage with default values of new options
|
||||||
return this.setAll({
|
return this.setAll(newOpts);
|
||||||
...oldOpts
|
|
||||||
, ...newOpts
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,13 +5,17 @@ export interface TypedEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TypedEventTarget<T extends TypedEvents> extends EventTarget {
|
export class TypedEventTarget<T extends TypedEvents> extends EventTarget {
|
||||||
|
// @ts-ignore
|
||||||
public addEventListener<K extends keyof T> (
|
public addEventListener<K extends keyof T> (
|
||||||
type: K, listener: (ev: CustomEvent<T[K]>) => void): void {
|
type: K, listener: (ev: CustomEvent<T[K]>) => void): void {
|
||||||
|
// @ts-ignore
|
||||||
super.addEventListener(type as string, listener);
|
super.addEventListener(type as string, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
public removeEventListener<K extends keyof T> (
|
public removeEventListener<K extends keyof T> (
|
||||||
type: K, listener: (ev: CustomEvent<T[K]>) => void): void {
|
type: K, listener: (ev: CustomEvent<T[K]>) => void): void {
|
||||||
|
// @ts-ignore
|
||||||
super.removeEventListener(type as string, listener);
|
super.removeEventListener(type as string, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const UA_CHROME_LEGACY = "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.288
|
|||||||
const UA_SAFARI = "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15";
|
const UA_SAFARI = "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15";
|
||||||
|
|
||||||
|
|
||||||
function getPlatformComponent (platform: string): string {
|
function getPlatformComponent (platform: string): string | undefined {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case "mac": return PLATFORM_MAC; break;
|
case "mac": return PLATFORM_MAC; break;
|
||||||
case "win": return PLATFORM_WIN; break;
|
case "win": return PLATFORM_WIN; break;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import logger from "./logger";
|
||||||
|
|
||||||
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
|
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
|
||||||
|
|
||||||
|
|
||||||
@@ -10,6 +12,8 @@ export function getNextEllipsis (ellipsis: string): string {
|
|||||||
if (ellipsis === "..") return "...";
|
if (ellipsis === "..") return "...";
|
||||||
if (ellipsis === "...") return "";
|
if (ellipsis === "...") return "";
|
||||||
/* tslint:enable:curly */
|
/* tslint:enable:curly */
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,7 +83,7 @@ export function getMediaTypesForPageUrl (
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface WindowCenteredProps {
|
export interface WindowCenteredProps {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
left: number;
|
left: number;
|
||||||
@@ -91,6 +95,11 @@ export function getWindowCenteredProps (
|
|||||||
, width: number
|
, width: number
|
||||||
, height: number): WindowCenteredProps {
|
, height: number): WindowCenteredProps {
|
||||||
|
|
||||||
|
if (refWin.left === undefined || refWin.width === undefined
|
||||||
|
|| refWin.top === undefined || refWin.height === undefined) {
|
||||||
|
throw logger.error("refWin missing positional attributes.");
|
||||||
|
}
|
||||||
|
|
||||||
const centerX = refWin.left + (refWin.width / 2);
|
const centerX = refWin.left + (refWin.width / 2);
|
||||||
const centerY = refWin.top + (refWin.height / 3);
|
const centerY = refWin.top + (refWin.height / 3);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import logger from "../../lib/logger";
|
||||||
import options from "../../lib/options";
|
import options from "../../lib/options";
|
||||||
import cast, { ensureInit } from "../../shim/export";
|
import cast, { ensureInit } from "../../shim/export";
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ import { Message, Receiver } from "../../types";
|
|||||||
|
|
||||||
function getLocalAddress () {
|
function getLocalAddress () {
|
||||||
const pc = new RTCPeerConnection();
|
const pc = new RTCPeerConnection();
|
||||||
pc.createDataChannel(null);
|
pc.createDataChannel("");
|
||||||
pc.createOffer().then(pc.setLocalDescription.bind(pc));
|
pc.createOffer().then(pc.setLocalDescription.bind(pc));
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -90,6 +91,9 @@ function getSession (opts: InitOptions): Promise<cast.Session> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Handle this
|
||||||
|
function sessionListener () {}
|
||||||
|
|
||||||
function onRequestSessionSuccess (session: cast.Session) {
|
function onRequestSessionSuccess (session: cast.Session) {
|
||||||
resolve(session);
|
resolve(session);
|
||||||
}
|
}
|
||||||
@@ -103,7 +107,7 @@ function getSession (opts: InitOptions): Promise<cast.Session> {
|
|||||||
|
|
||||||
const apiConfig = new cast.ApiConfig(
|
const apiConfig = new cast.ApiConfig(
|
||||||
sessionRequest
|
sessionRequest
|
||||||
, null // sessionListener
|
, sessionListener // sessionListener
|
||||||
, receiverListener); // receiverListener
|
, receiverListener); // receiverListener
|
||||||
|
|
||||||
|
|
||||||
@@ -112,7 +116,7 @@ function getSession (opts: InitOptions): Promise<cast.Session> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getMedia (opts: InitOptions): Promise<cast.media.Media> {
|
function getMedia (opts: InitOptions): Promise<cast.media.Media> {
|
||||||
return new Promise(async resolve => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let mediaUrl = new URL(opts.mediaUrl);
|
let mediaUrl = new URL(opts.mediaUrl);
|
||||||
let subtitleUrls: URL[] = [];
|
let subtitleUrls: URL[] = [];
|
||||||
|
|
||||||
@@ -137,14 +141,13 @@ function getMedia (opts: InitOptions): Promise<cast.media.Media> {
|
|||||||
path => new URL(path, baseUrl));
|
path => new URL(path, baseUrl));
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to start media server");
|
throw logger.error("Failed to start media server");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const activeTrackIds: number[] = [];
|
const activeTrackIds: number[] = [];
|
||||||
const mediaInfo = new cast.media.MediaInfo(mediaUrl.href, null);
|
const mediaInfo = new cast.media.MediaInfo(mediaUrl.href, "");
|
||||||
|
|
||||||
mediaInfo.metadata = new cast.media.GenericMediaMetadata();
|
mediaInfo.metadata = new cast.media.GenericMediaMetadata();
|
||||||
mediaInfo.metadata.metadataType = cast.media.MetadataType.GENERIC;
|
mediaInfo.metadata.metadataType = cast.media.MetadataType.GENERIC;
|
||||||
@@ -222,7 +225,7 @@ function getMedia (opts: InitOptions): Promise<cast.media.Media> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add track to mediaInfo
|
// Add track to mediaInfo
|
||||||
mediaInfo.tracks.push(castTrack);
|
mediaInfo.tracks?.push(castTrack);
|
||||||
|
|
||||||
// If enabled, mark as active track for load request
|
// If enabled, mark as active track for load request
|
||||||
if (track.mode === "showing" || trackElement.default) {
|
if (track.mode === "showing" || trackElement.default) {
|
||||||
@@ -238,9 +241,7 @@ function getMedia (opts: InitOptions): Promise<cast.media.Media> {
|
|||||||
loadRequest.autoplay = false;
|
loadRequest.autoplay = false;
|
||||||
loadRequest.activeTrackIds = activeTrackIds;
|
loadRequest.activeTrackIds = activeTrackIds;
|
||||||
|
|
||||||
currentSession.loadMedia(loadRequest
|
currentSession.loadMedia(loadRequest, resolve, reject);
|
||||||
, (media) => resolve(media)
|
|
||||||
, null);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,11 +267,11 @@ async function registerMediaElementListeners () {
|
|||||||
|
|
||||||
|
|
||||||
mediaElement.addEventListener("play", () => {
|
mediaElement.addEventListener("play", () => {
|
||||||
currentMedia.play(null, null, null);
|
currentMedia.play();
|
||||||
});
|
});
|
||||||
|
|
||||||
mediaElement.addEventListener("pause", () => {
|
mediaElement.addEventListener("pause", () => {
|
||||||
currentMedia.pause(null, null, null);
|
currentMedia.pause();
|
||||||
});
|
});
|
||||||
|
|
||||||
mediaElement.addEventListener("suspend", () => {
|
mediaElement.addEventListener("suspend", () => {
|
||||||
@@ -281,7 +282,7 @@ async function registerMediaElementListeners () {
|
|||||||
const seekRequest = new cast.media.SeekRequest();
|
const seekRequest = new cast.media.SeekRequest();
|
||||||
seekRequest.currentTime = mediaElement.currentTime;
|
seekRequest.currentTime = mediaElement.currentTime;
|
||||||
|
|
||||||
currentMedia.seek(seekRequest, null, null);
|
currentMedia.seek(seekRequest);
|
||||||
});
|
});
|
||||||
|
|
||||||
mediaElement.addEventListener("ratechange", () => {
|
mediaElement.addEventListener("ratechange", () => {
|
||||||
@@ -394,7 +395,7 @@ export async function init (opts: InitOptions) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (await options.get("mediaStopOnUnload")) {
|
if (await options.get("mediaStopOnUnload")) {
|
||||||
currentSession.stop(null, null);
|
currentSession.stop(() => {}, () => {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,11 +202,11 @@ class AudioPlayerElement extends PlayerElement {}
|
|||||||
class VideoPlayerElement extends PlayerElement {
|
class VideoPlayerElement extends PlayerElement {
|
||||||
set overlayHidden (val: boolean) {
|
set overlayHidden (val: boolean) {
|
||||||
const shadowRoot = internalShadowRoots.get(this);
|
const shadowRoot = internalShadowRoots.get(this);
|
||||||
(shadowRoot.querySelector(".overlay") as HTMLDivElement).hidden = val;
|
(shadowRoot?.querySelector(".overlay") as HTMLDivElement).hidden = val;
|
||||||
}
|
}
|
||||||
get overlayHidden () {
|
get overlayHidden () {
|
||||||
const shadowRoot = internalShadowRoots.get(this);
|
const shadowRoot = internalShadowRoots.get(this);
|
||||||
return (shadowRoot.querySelector(".overlay") as HTMLDivElement).hidden;
|
return (shadowRoot?.querySelector(".overlay") as HTMLDivElement).hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function sendAppMessage (subject: string, data: any) {
|
|||||||
session.sendMessage(FX_CAST_RECEIVER_APP_NAMESPACE, {
|
session.sendMessage(FX_CAST_RECEIVER_APP_NAMESPACE, {
|
||||||
subject
|
subject
|
||||||
, data
|
, data
|
||||||
}, null, null);
|
}, () => {}, () => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -76,6 +76,11 @@ async function onRequestSessionSuccess (newSession: cast.Session) {
|
|||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Shouldn't be possible
|
||||||
|
if (!ctx) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Set initial size
|
// Set initial size
|
||||||
canvas.width = window.innerWidth;
|
canvas.width = window.innerWidth;
|
||||||
canvas.height = window.innerHeight;
|
canvas.height = window.innerHeight;
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
export default class DialRequest {
|
export default class DialRequest {
|
||||||
constructor (
|
constructor (
|
||||||
public appName: string
|
public appName: string
|
||||||
, public launchParameter: string = null) {
|
, public launchParameter: (string | null) = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
export default class Error {
|
export default class Error {
|
||||||
constructor (
|
constructor (
|
||||||
public code: string
|
public code: string
|
||||||
, public description: string = null
|
, public description: (string | null) = null
|
||||||
, public details: any = null) {
|
, public details: any = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Image
|
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Image
|
||||||
export default class Image {
|
export default class Image {
|
||||||
public width: number = null;
|
public width: (number | null) = null;
|
||||||
public height: number = null;
|
public height: (number | null) = null;
|
||||||
|
|
||||||
constructor (public url: string) {}
|
constructor (public url: string) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import { ReceiverType } from "../enums";
|
|||||||
|
|
||||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Receiver
|
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Receiver
|
||||||
export default class Receiver {
|
export default class Receiver {
|
||||||
public displayStatus: ReceiverDisplayStatus = null;
|
public displayStatus: (ReceiverDisplayStatus | null) = null;
|
||||||
public isActiveInput: boolean = null;
|
public isActiveInput: (boolean | null) = null;
|
||||||
public receiverType: string = ReceiverType.CAST;
|
public receiverType: string = ReceiverType.CAST;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public label: string
|
public label: string
|
||||||
, public friendlyName: string
|
, public friendlyName: string
|
||||||
, public capabilities: string[] = []
|
, public capabilities: string[] = []
|
||||||
, public volume: Volume = null) {
|
, public volume: (Volume | null) = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Image from "./Image";
|
|||||||
|
|
||||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.ReceiverDisplayStatus
|
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.ReceiverDisplayStatus
|
||||||
export default class ReceiverDisplayStatus {
|
export default class ReceiverDisplayStatus {
|
||||||
public showStop: boolean = null;
|
public showStop: (boolean | null) = null;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public statusText: string
|
public statusText: string
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.SenderApplication
|
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.SenderApplication
|
||||||
export default class SenderApplication {
|
export default class SenderApplication {
|
||||||
public packageId: string = null;
|
public packageId: (string | null) = null;
|
||||||
public url: string = null;
|
public url: (string | null) = null;
|
||||||
|
|
||||||
constructor (public platform: string) {}
|
constructor (public platform: string) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default class Session {
|
|||||||
public namespaces: Array<{ name: "string" }>;
|
public namespaces: Array<{ name: "string" }>;
|
||||||
public senderApps: SenderApplication[];
|
public senderApps: SenderApplication[];
|
||||||
public status: SessionStatus;
|
public status: SessionStatus;
|
||||||
public statusText: string;
|
public statusText: string | null;
|
||||||
public transportId: string;
|
public transportId: string;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@@ -102,12 +102,15 @@ export default class Session {
|
|||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "shim:/session/stopped": {
|
case "shim:/session/stopped": {
|
||||||
// Disconnect from extension messages
|
// Disconnect from extension messages
|
||||||
_listener.get(this).disconnect();
|
_listener.get(this)?.disconnect();
|
||||||
|
|
||||||
this.status = SessionStatus.STOPPED;
|
this.status = SessionStatus.STOPPED;
|
||||||
|
|
||||||
for (const listener of _updateListeners.get(this)) {
|
const updateListeners = _updateListeners.get(this);
|
||||||
listener(false);
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -147,8 +150,11 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const listener of _updateListeners.get(this)) {
|
const updateListeners = _updateListeners.get(this);
|
||||||
listener(true);
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -156,19 +162,28 @@ export default class Session {
|
|||||||
|
|
||||||
|
|
||||||
case "shim:/session/impl_addMessageListener": {
|
case "shim:/session/impl_addMessageListener": {
|
||||||
const { namespace, data } = message.data;
|
const { namespace, data }
|
||||||
for (const listener of
|
: { namespace: string, data: string } = message.data;
|
||||||
_messageListeners.get(this).get(namespace)) {
|
|
||||||
listener(namespace, data);
|
const messageListeners = _messageListeners
|
||||||
|
.get(this)?.get(namespace);
|
||||||
|
|
||||||
|
if (messageListeners) {
|
||||||
|
for (const listener of messageListeners) {
|
||||||
|
listener(namespace, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "shim:/session/impl_sendMessage": {
|
case "shim:/session/impl_sendMessage": {
|
||||||
const { messageId, error } = message.data;
|
const { messageId, error }
|
||||||
const [ successCallback, errorCallback ]
|
: { messageId: string, error: boolean } = message.data;
|
||||||
= _sendMessageCallbacks.get(this).get(messageId);
|
|
||||||
|
const [ successCallback, errorCallback ] =
|
||||||
|
_sendMessageCallbacks
|
||||||
|
.get(this)?.get(messageId) ?? [];
|
||||||
|
|
||||||
if (error && errorCallback) {
|
if (error && errorCallback) {
|
||||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||||
@@ -176,17 +191,16 @@ export default class Session {
|
|||||||
successCallback();
|
successCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendMessageCallbacks.get(this).delete(messageId);
|
_sendMessageCallbacks.get(this)?.delete(messageId);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "shim:/session/impl_setReceiverMuted": {
|
case "shim:/session/impl_setReceiverMuted": {
|
||||||
const { volumeId, error } = message.data;
|
const { volumeId, error } = message.data;
|
||||||
const [ successCallback, errorCallback ]
|
const [ successCallback, errorCallback ] =
|
||||||
= _setReceiverMutedCallbacks
|
_setReceiverMutedCallbacks
|
||||||
.get(this)
|
.get(this)?.get(volumeId) ?? [];
|
||||||
.get(volumeId);
|
|
||||||
|
|
||||||
if (error && errorCallback) {
|
if (error && errorCallback) {
|
||||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||||
@@ -194,16 +208,16 @@ export default class Session {
|
|||||||
successCallback();
|
successCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
_setReceiverMutedCallbacks.get(this).delete(volumeId);
|
_setReceiverMutedCallbacks.get(this)?.delete(volumeId);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "shim:/session/impl_setReceiverVolumeLevel": {
|
case "shim:/session/impl_setReceiverVolumeLevel": {
|
||||||
const { volumeId, error } = message.data;
|
const { volumeId, error } = message.data;
|
||||||
const [ successCallback, errorCallback ]
|
const [ successCallback, errorCallback ] =
|
||||||
= _setReceiverVolumeLevelCallbacks.get(this)
|
_setReceiverVolumeLevelCallbacks
|
||||||
.get(volumeId);
|
.get(this)?.get(volumeId) ?? [];
|
||||||
|
|
||||||
if (error && errorCallback) {
|
if (error && errorCallback) {
|
||||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||||
@@ -211,7 +225,8 @@ export default class Session {
|
|||||||
successCallback();
|
successCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
_setReceiverVolumeLevelCallbacks.get(this).delete(volumeId);
|
_setReceiverVolumeLevelCallbacks
|
||||||
|
.get(this)?.delete(volumeId);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -219,18 +234,21 @@ export default class Session {
|
|||||||
case "shim:/session/impl_stop": {
|
case "shim:/session/impl_stop": {
|
||||||
const { stopId, error } = message.data;
|
const { stopId, error } = message.data;
|
||||||
const [ successCallback, errorCallback ]
|
const [ successCallback, errorCallback ]
|
||||||
= _stopCallbacks.get(this).get(stopId);
|
= _stopCallbacks.get(this)?.get(stopId) ?? [];
|
||||||
|
|
||||||
// Disconnect from extension messages
|
// Disconnect from extension messages
|
||||||
_listener.get(this).disconnect();
|
_listener.get(this)?.disconnect();
|
||||||
|
|
||||||
if (error && errorCallback) {
|
if (error && errorCallback) {
|
||||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||||
} else {
|
} else {
|
||||||
this.status = SessionStatus.STOPPED;
|
this.status = SessionStatus.STOPPED;
|
||||||
|
|
||||||
for (const listener of _updateListeners.get(this)) {
|
const updateListeners = _updateListeners.get(this);
|
||||||
listener(false);
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successCallback) {
|
if (successCallback) {
|
||||||
@@ -238,7 +256,7 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopCallbacks.get(this).delete(stopId);
|
_stopCallbacks.get(this)?.delete(stopId);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -257,11 +275,11 @@ export default class Session {
|
|||||||
namespace: string
|
namespace: string
|
||||||
, listener: MessageListener) {
|
, listener: MessageListener) {
|
||||||
|
|
||||||
if (!_messageListeners.get(this).has(namespace)) {
|
if (!_messageListeners.get(this)?.has(namespace)) {
|
||||||
_messageListeners.get(this).set(namespace, new Set());
|
_messageListeners.get(this)?.set(namespace, new Set());
|
||||||
}
|
}
|
||||||
|
|
||||||
_messageListeners.get(this).get(namespace).add(listener);
|
_messageListeners.get(this)?.get(namespace)?.add(listener);
|
||||||
|
|
||||||
sendMessageResponse({
|
sendMessageResponse({
|
||||||
subject: "bridge:/session/impl_addMessageListener"
|
subject: "bridge:/session/impl_addMessageListener"
|
||||||
@@ -271,7 +289,7 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addUpdateListener (listener: UpdateListener) {
|
public addUpdateListener (listener: UpdateListener) {
|
||||||
_updateListeners.get(this).add(listener);
|
_updateListeners.get(this)?.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public leave (
|
public leave (
|
||||||
@@ -286,7 +304,7 @@ export default class Session {
|
|||||||
, _id: _id.get(this)
|
, _id: _id.get(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
_leaveCallbacks.get(this).set(id, [
|
_leaveCallbacks.get(this)?.set(id, [
|
||||||
successCallback
|
successCallback
|
||||||
, errorCallback
|
, errorCallback
|
||||||
]);
|
]);
|
||||||
@@ -322,12 +340,17 @@ export default class Session {
|
|||||||
const message = JSON.parse(data);
|
const message = JSON.parse(data);
|
||||||
|
|
||||||
if (message.status && message.status.length > 0) {
|
if (message.status && message.status.length > 0) {
|
||||||
|
const sessionId = _id.get(this);
|
||||||
|
if (!sessionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
hasResponded = true;
|
hasResponded = true;
|
||||||
|
|
||||||
const media = new Media(
|
const media = new Media(
|
||||||
this.sessionId
|
this.sessionId
|
||||||
, message.status[0].mediaSessionId
|
, message.status[0].mediaSessionId
|
||||||
, _id.get(this));
|
, sessionId);
|
||||||
|
|
||||||
media.media = loadRequest.media;
|
media.media = loadRequest.media;
|
||||||
this.media = [ media ];
|
this.media = [ media ];
|
||||||
@@ -361,14 +384,14 @@ export default class Session {
|
|||||||
namespace: string
|
namespace: string
|
||||||
, listener: MessageListener): void {
|
, listener: MessageListener): void {
|
||||||
|
|
||||||
_messageListeners.get(this).get(namespace).delete(listener);
|
_messageListeners.get(this)?.get(namespace)?.delete(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeUpdateListener (
|
public removeUpdateListener (
|
||||||
_namespace: string
|
_namespace: string
|
||||||
, listener: UpdateListener): void {
|
, listener: UpdateListener): void {
|
||||||
|
|
||||||
_updateListeners.get(this).delete(listener);
|
_updateListeners.get(this)?.delete(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendMessage (
|
public sendMessage (
|
||||||
@@ -389,7 +412,7 @@ export default class Session {
|
|||||||
, _id: _id.get(this)
|
, _id: _id.get(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
_sendMessageCallbacks.get(this).set(messageId, [
|
_sendMessageCallbacks.get(this)?.set(messageId, [
|
||||||
successCallback
|
successCallback
|
||||||
, errorCallback
|
, errorCallback
|
||||||
]);
|
]);
|
||||||
@@ -408,7 +431,7 @@ export default class Session {
|
|||||||
, _id: _id.get(this)
|
, _id: _id.get(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
_setReceiverMutedCallbacks.get(this).set(volumeId, [
|
_setReceiverMutedCallbacks.get(this)?.set(volumeId, [
|
||||||
successCallback
|
successCallback
|
||||||
, errorCallback
|
, errorCallback
|
||||||
]);
|
]);
|
||||||
@@ -427,7 +450,7 @@ export default class Session {
|
|||||||
, _id: _id.get(this)
|
, _id: _id.get(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
_setReceiverVolumeLevelCallbacks.get(this).set(volumeId, [
|
_setReceiverVolumeLevelCallbacks.get(this)?.set(volumeId, [
|
||||||
successCallback
|
successCallback
|
||||||
, errorCallback
|
, errorCallback
|
||||||
]);
|
]);
|
||||||
@@ -445,7 +468,7 @@ export default class Session {
|
|||||||
, _id: _id.get(this)
|
, _id: _id.get(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
_stopCallbacks.get(this).set(stopId, [
|
_stopCallbacks.get(this)?.set(stopId, [
|
||||||
successCallback
|
successCallback
|
||||||
, errorCallback
|
, errorCallback
|
||||||
]);
|
]);
|
||||||
@@ -456,6 +479,6 @@ export default class Session {
|
|||||||
this.sendMessage(
|
this.sendMessage(
|
||||||
"urn:x-cast:com.google.cast.media"
|
"urn:x-cast:com.google.cast.media"
|
||||||
, message
|
, message
|
||||||
, null, null);
|
, () => {}, () => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Timeout from "./Timeout";
|
|||||||
|
|
||||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.SessionRequest
|
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.SessionRequest
|
||||||
export default class SessionRequest {
|
export default class SessionRequest {
|
||||||
public language: string = null;
|
public language: (string | null) = null;
|
||||||
public dialRequest: any = null;
|
public dialRequest: any = null;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { VolumeControlType } from "../enums";
|
|||||||
|
|
||||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Volume
|
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Volume
|
||||||
export default class Volume {
|
export default class Volume {
|
||||||
public controlType: VolumeControlType;
|
public controlType?: VolumeControlType;
|
||||||
public stepInterval: number;
|
public stepInterval?: number;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public level: number = null
|
public level: (number | null) = null
|
||||||
, public muted: boolean = null) {
|
, public muted: (boolean | null) = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,8 +129,8 @@ export function removeReceiverActionListener (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function requestSession (
|
export function requestSession (
|
||||||
successCallback?: RequestSessionSuccessCallback
|
successCallback: RequestSessionSuccessCallback
|
||||||
, errorCallback?: ErrorCallback
|
, errorCallback: ErrorCallback
|
||||||
, _sessionRequest: SessionRequest = apiConfig.sessionRequest): void {
|
, _sessionRequest: SessionRequest = apiConfig.sessionRequest): void {
|
||||||
|
|
||||||
console.info("fx_cast (Debug): cast.requestSession");
|
console.info("fx_cast (Debug): cast.requestSession");
|
||||||
@@ -176,8 +176,8 @@ export function requestSession (
|
|||||||
|
|
||||||
export function _requestSession (
|
export function _requestSession (
|
||||||
_receiver: Receiver
|
_receiver: Receiver
|
||||||
, successCallback?: RequestSessionSuccessCallback
|
, successCallback: RequestSessionSuccessCallback
|
||||||
, errorCallback?: ErrorCallback): void {
|
, errorCallback: ErrorCallback): void {
|
||||||
|
|
||||||
console.info("fx_cast (Debug): cast._requestSession");
|
console.info("fx_cast (Debug): cast._requestSession");
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ export function _requestSession (
|
|||||||
const lastSession = sessionList[sessionList.length - 1];
|
const lastSession = sessionList[sessionList.length - 1];
|
||||||
|
|
||||||
if (lastSession.status !== SessionStatus.STOPPED) {
|
if (lastSession.status !== SessionStatus.STOPPED) {
|
||||||
lastSession.stop(createSession, null);
|
lastSession.stop(createSession, () => {});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
createSession();
|
createSession();
|
||||||
@@ -354,7 +354,7 @@ onMessage(async message => {
|
|||||||
const lastSession = sessionList[sessionList.length - 1];
|
const lastSession = sessionList[sessionList.length - 1];
|
||||||
|
|
||||||
if (lastSession.status !== SessionStatus.STOPPED) {
|
if (lastSession.status !== SessionStatus.STOPPED) {
|
||||||
lastSession.stop(createSession, null);
|
lastSession.stop(createSession, () => {});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
createSession();
|
createSession();
|
||||||
@@ -380,9 +380,11 @@ onMessage(async message => {
|
|||||||
|
|
||||||
case "shim:/launchApp": {
|
case "shim:/launchApp": {
|
||||||
const receiver: Receiver = message.data.receiver;
|
const receiver: Receiver = message.data.receiver;
|
||||||
_requestSession(receiver, session => {
|
_requestSession(receiver
|
||||||
apiConfig.sessionListener(session);
|
, session => {
|
||||||
});
|
apiConfig.sessionListener(session);
|
||||||
|
}
|
||||||
|
, () => {});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default class EditTracksInfoRequest {
|
|||||||
public requestId = 0;
|
public requestId = 0;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public activeTrackIds: number[] = null
|
public activeTrackIds: (number[] | null) = null
|
||||||
, public textTrackStyle: string = null) {
|
, public textTrackStyle: (string | null) = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import { MetadataType } from "../enums";
|
|||||||
|
|
||||||
|
|
||||||
export default class GenericMediaMetadata {
|
export default class GenericMediaMetadata {
|
||||||
public images: Image[] = undefined;
|
public images: (Image[] | undefined) = undefined;
|
||||||
public metadataType: number = MetadataType.GENERIC;
|
public metadataType: number = MetadataType.GENERIC;
|
||||||
public releaseDate: string = undefined;
|
public releaseDate: (string | undefined) = undefined;
|
||||||
public releaseYear: number = undefined;
|
public releaseYear: (number | undefined) = undefined;
|
||||||
public subtitle: string = undefined;
|
public subtitle: (string | undefined) = undefined;
|
||||||
public title: string = undefined;
|
public title: (string | undefined) = undefined;
|
||||||
public type: number = MetadataType.GENERIC;
|
public type: number = MetadataType.GENERIC;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import MediaInfo from "./MediaInfo";
|
|||||||
|
|
||||||
|
|
||||||
export default class LoadRequest {
|
export default class LoadRequest {
|
||||||
public activeTrackIds: number[] = null;
|
public activeTrackIds: (number[] | null) = null;
|
||||||
public autoplay: boolean = true;
|
public autoplay: (boolean | null) = true;
|
||||||
public currentTime: number = null;
|
public currentTime: (number | null) = null;
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public media: MediaInfo;
|
public media: MediaInfo;
|
||||||
public requestId: number = 0;
|
public requestId: number = 0;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public type: string = "LOAD";
|
public type: string = "LOAD";
|
||||||
|
|
||||||
constructor (mediaInfo: MediaInfo) {
|
constructor (mediaInfo: MediaInfo) {
|
||||||
|
|||||||
@@ -40,17 +40,17 @@ const _lastCurrentTime = new WeakMap<Media, number>();
|
|||||||
|
|
||||||
|
|
||||||
export default class Media {
|
export default class Media {
|
||||||
public activeTrackIds: number[] = null;
|
public activeTrackIds: (number[] | null) = null;
|
||||||
public currentItemId: number = null;
|
public currentItemId: (number | null) = null;
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public currentTime: number = 0;
|
public currentTime: number = 0;
|
||||||
public idleReason: string = null;
|
public idleReason: (string | null) = null;
|
||||||
public items: QueueItem[] = null;
|
public items: (QueueItem[] | null) = null;
|
||||||
public loadingItemId: number = null;
|
public loadingItemId: (number | null) = null;
|
||||||
public media: MediaInfo = null;
|
public media: (MediaInfo | null) = null;
|
||||||
public playbackRate: number = 1;
|
public playbackRate: number = 1;
|
||||||
public playerState: string = PlayerState.IDLE;
|
public playerState: string = PlayerState.IDLE;
|
||||||
public preloadedItemId: number = null;
|
public preloadedItemId: (number | null) = null;
|
||||||
public repeatMode: string = RepeatMode.OFF;
|
public repeatMode: string = RepeatMode.OFF;
|
||||||
public supportedMediaCommands: string[] = [];
|
public supportedMediaCommands: string[] = [];
|
||||||
public volume: Volume = new Volume();
|
public volume: Volume = new Volume();
|
||||||
@@ -66,8 +66,6 @@ export default class Media {
|
|||||||
_updateListeners.set(this, new Set());
|
_updateListeners.set(this, new Set());
|
||||||
_sendMediaMessageCallbacks.set(this, new Map());
|
_sendMediaMessageCallbacks.set(this, new Map());
|
||||||
|
|
||||||
_lastCurrentTime.set(this, undefined);
|
|
||||||
|
|
||||||
|
|
||||||
sendMessageResponse({
|
sendMessageResponse({
|
||||||
subject: "bridge:/media/initialize"
|
subject: "bridge:/media/initialize"
|
||||||
@@ -109,19 +107,23 @@ export default class Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call update listeners
|
// Call update listeners
|
||||||
for (const listener of _updateListeners.get(this)) {
|
const updateListeners = _updateListeners.get(this);
|
||||||
listener(true);
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "shim:/media/sendMediaMessageResponse": {
|
case "shim:/media/sendMediaMessageResponse": {
|
||||||
const { messageId, error } = message.data;
|
const { messageId, error }
|
||||||
|
: { messageId: string, error: any } = message.data;
|
||||||
|
|
||||||
const [ successCallback, errorCallback ]
|
const [ successCallback, errorCallback ]
|
||||||
= _sendMediaMessageCallbacks
|
= _sendMediaMessageCallbacks
|
||||||
.get(this)
|
.get(this)?.get(messageId) ?? [];
|
||||||
.get(messageId);
|
|
||||||
|
|
||||||
if (error && errorCallback) {
|
if (error && errorCallback) {
|
||||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||||
@@ -137,7 +139,7 @@ export default class Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addUpdateListener (listener: UpdateListener): void {
|
public addUpdateListener (listener: UpdateListener): void {
|
||||||
_updateListeners.get(this).add(listener);
|
_updateListeners.get(this)?.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public editTracksInfo (
|
public editTracksInfo (
|
||||||
@@ -149,12 +151,13 @@ export default class Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getEstimatedTime (): number {
|
public getEstimatedTime (): number {
|
||||||
if (!this.currentTime) {
|
const lastTime = _lastCurrentTime.get(this);
|
||||||
|
|
||||||
|
if (this.currentTime === undefined || lastTime === undefined) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.currentTime
|
return this.currentTime + ((Date.now() / 1000) - lastTime);
|
||||||
+ ((Date.now() / 1000) - _lastCurrentTime.get(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStatus (
|
public getStatus (
|
||||||
@@ -167,7 +170,7 @@ export default class Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public pause (
|
public pause (
|
||||||
_pauseRequest: PauseRequest
|
_pauseRequest?: PauseRequest
|
||||||
, successCallback?: SuccessCallback
|
, successCallback?: SuccessCallback
|
||||||
, errorCallback?: ErrorCallback): void {
|
, errorCallback?: ErrorCallback): void {
|
||||||
|
|
||||||
@@ -254,7 +257,7 @@ export default class Media {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public removeUpdateListener (listener: UpdateListener) {
|
public removeUpdateListener (listener: UpdateListener) {
|
||||||
_updateListeners.get(this).delete(listener);
|
_updateListeners.get(this)?.delete(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public seek (
|
public seek (
|
||||||
@@ -307,7 +310,7 @@ export default class Media {
|
|||||||
|
|
||||||
const messageId = uuid();
|
const messageId = uuid();
|
||||||
|
|
||||||
_sendMediaMessageCallbacks.get(this).set(messageId, [
|
_sendMediaMessageCallbacks.get(this)?.set(messageId, [
|
||||||
successCallback
|
successCallback
|
||||||
, errorCallback
|
, errorCallback
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ type Metadata =
|
|||||||
| TvShowMediaMetadata;
|
| TvShowMediaMetadata;
|
||||||
|
|
||||||
export default class MediaInfo {
|
export default class MediaInfo {
|
||||||
public customData: string = null;
|
public customData: any = null;
|
||||||
public duration: number = null;
|
public duration: (number | null) = null;
|
||||||
public metadata: Metadata = null;
|
public metadata: (Metadata | null) = null;
|
||||||
public streamType: string = StreamType.BUFFERED;
|
public streamType: string = StreamType.BUFFERED;
|
||||||
public textTrackStyle: TextTrackStyle = null;
|
public textTrackStyle: (TextTrackStyle | null) = null;
|
||||||
public tracks: Track[] = null;
|
public tracks: (Track[] | null) = null;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public contentId: string
|
public contentId: string
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import { MetadataType } from "../enums";
|
|||||||
|
|
||||||
|
|
||||||
export default class MovieMediaMetadata {
|
export default class MovieMediaMetadata {
|
||||||
public images: Image[] = undefined;
|
public images: (Image[] | undefined) = undefined;
|
||||||
public metadataType: number = MetadataType.MOVIE;
|
public metadataType: number = MetadataType.MOVIE;
|
||||||
public releaseDate: string = undefined;
|
public releaseDate: (string | undefined) = undefined;
|
||||||
public releaseYear: number = undefined;
|
public releaseYear: (number | undefined) = undefined;
|
||||||
public studio: string = undefined;
|
public studio: (string | undefined) = undefined;
|
||||||
public subtitle: string = undefined;
|
public subtitle: (string | undefined) = undefined;
|
||||||
public title: string = undefined;
|
public title: (string | undefined) = undefined;
|
||||||
public type: number = MetadataType.MOVIE;
|
public type: number = MetadataType.MOVIE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ import { MetadataType } from "../enums";
|
|||||||
|
|
||||||
|
|
||||||
export default class MusicTrackMediaMetadata {
|
export default class MusicTrackMediaMetadata {
|
||||||
public albumArtist: string = undefined;
|
public albumArtist: (string | undefined) = undefined;
|
||||||
public albumName: string = undefined;
|
public albumName: (string | undefined) = undefined;
|
||||||
public artist: string = undefined;
|
public artist: (string | undefined) = undefined;
|
||||||
public artistName: string = undefined;
|
public artistName: (string | undefined) = undefined;
|
||||||
public composer: string = undefined;
|
public composer: (string | undefined) = undefined;
|
||||||
public discNumber: number = undefined;
|
public discNumber: (number | undefined) = undefined;
|
||||||
public images: Image[] = undefined;
|
public images: (Image[] | undefined) = undefined;
|
||||||
public metadataType: number = MetadataType.MUSIC_TRACK;
|
public metadataType: number = MetadataType.MUSIC_TRACK;
|
||||||
public releaseDate: string = undefined;
|
public releaseDate: (string | undefined) = undefined;
|
||||||
public releaseYear: number = undefined;
|
public releaseYear: (number | undefined) = undefined;
|
||||||
public songName: string = undefined;
|
public songName: (string | undefined) = undefined;
|
||||||
public title: string = undefined;
|
public title: (string | undefined) = undefined;
|
||||||
public trackNumber: number = undefined;
|
public trackNumber: (number | undefined) = undefined;
|
||||||
public type: number = MetadataType.MUSIC_TRACK;
|
public type: number = MetadataType.MUSIC_TRACK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ import { MetadataType } from "../enums";
|
|||||||
|
|
||||||
|
|
||||||
export default class PhotoMediaMetadata {
|
export default class PhotoMediaMetadata {
|
||||||
public artist: string = undefined;
|
public artist: (string | undefined) = undefined;
|
||||||
public creationDateTime: string = undefined;
|
public creationDateTime: (string | undefined) = undefined;
|
||||||
public height: number = undefined;
|
public height: (number | undefined) = undefined;
|
||||||
public images: Image[] = undefined;
|
public images: (Image[] | undefined) = undefined;
|
||||||
public latitude: number = undefined;
|
public latitude: (number | undefined) = undefined;
|
||||||
public location: string = undefined;
|
public location: (string | undefined) = undefined;
|
||||||
public longitude: number = undefined;
|
public longitude: (number | undefined) = undefined;
|
||||||
public metadataType: number = MetadataType.PHOTO;
|
public metadataType: number = MetadataType.PHOTO;
|
||||||
public title: string = undefined;
|
public title: (string | undefined) = undefined;
|
||||||
public type: number = MetadataType.PHOTO;
|
public type: number = MetadataType.PHOTO;
|
||||||
public width: number = undefined;
|
public width: (number | undefined) = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import QueueItem from "./QueueItem";
|
|||||||
|
|
||||||
export default class QueueInsertItemsRequest {
|
export default class QueueInsertItemsRequest {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public insertBefore: number = null;
|
public insertBefore: (number | null) = null;
|
||||||
public requestId: number = null;
|
public requestId: (number | null) = null;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public type: string = "QUEUE_INSERT";
|
public type: string = "QUEUE_INSERT";
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import MediaInfo from "./MediaInfo";
|
|||||||
|
|
||||||
|
|
||||||
export default class QueueItem {
|
export default class QueueItem {
|
||||||
public activeTrackIds: number[] = null;
|
public activeTrackIds: (number[] | null) = null;
|
||||||
public autoplay: boolean = true;
|
public autoplay: boolean = true;
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public itemId: number = null;
|
public itemId: (number | null) = null;
|
||||||
public media: MediaInfo;
|
public media: MediaInfo;
|
||||||
public playbackDuration: number = null;
|
public playbackDuration: (number | null) = null;
|
||||||
public preloadTime: number = 0;
|
public preloadTime: number = 0;
|
||||||
public startTime: number = 0;
|
public startTime: number = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { RepeatMode } from "../enums";
|
|||||||
export default class QueueLoadRequest {
|
export default class QueueLoadRequest {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public repeatMode: string = RepeatMode.OFF;
|
public repeatMode: string = RepeatMode.OFF;
|
||||||
public requestId: number = null;
|
public requestId: (number | null) = null;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public startIndex: number = 0;
|
public startIndex: number = 0;
|
||||||
public type: string = "QUEUE_LOAD";
|
public type: string = "QUEUE_LOAD";
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
export default class QueueRemoveItemsRequest {
|
export default class QueueRemoveItemsRequest {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public requestId: number = null;
|
public requestId: (number | null) = null;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public type: string = "QUEUE_REMOVE";
|
public type: string = "QUEUE_REMOVE";
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
export default class QueueReorderItemsRequest {
|
export default class QueueReorderItemsRequest {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public insertBefore: number = null;
|
public insertBefore: (number | null) = null;
|
||||||
public requestId: number = null;
|
public requestId: (number | null) = null;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public type: string = "QUEUE_REORDER";
|
public type: string = "QUEUE_REORDER";
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
export default class QueueSetPropertiesRequest {
|
export default class QueueSetPropertiesRequest {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public repeatMode: string = null;
|
public repeatMode: (string | null) = null;
|
||||||
public requestId: number = null;
|
public requestId: (number | null) = null;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public type: string = "QUEUE_UPDATE";
|
public type: string = "QUEUE_UPDATE";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import QueueItem from "./QueueItem";
|
|||||||
|
|
||||||
export default class QueueUpdateItemsRequest {
|
export default class QueueUpdateItemsRequest {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public requestId: number = null;
|
public requestId: (number | null) = null;
|
||||||
public sessionId: string = null;
|
public sessionId: (string | null) = null;
|
||||||
public type: string = "QUEUE_UPDATE";
|
public type: string = "QUEUE_UPDATE";
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
export default class SeekRequest {
|
export default class SeekRequest {
|
||||||
public currentTime: number = null;
|
public currentTime: (number | null) = null;
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public resumeState: string = null;
|
public resumeState: (string | null) = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
export default class TextTrackStyle {
|
export default class TextTrackStyle {
|
||||||
public backgroundColor: string = null;
|
public backgroundColor: (string | null) = null;
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public edgeColor: string = null;
|
public edgeColor: (string | null) = null;
|
||||||
public edgeType: string = null;
|
public edgeType: (string | null) = null;
|
||||||
public fontFamily: string = null;
|
public fontFamily: (string | null) = null;
|
||||||
public fontGenericFamily: string = null;
|
public fontGenericFamily: (string | null) = null;
|
||||||
public fontScale: number = null;
|
public fontScale: (number | null) = null;
|
||||||
public fontStyle: string = null;
|
public fontStyle: (string | null) = null;
|
||||||
public foregroundColor: string = null;
|
public foregroundColor: (string | null) = null;
|
||||||
public windowColor: string = null;
|
public windowColor: (string | null) = null;
|
||||||
public windowRoundedCornerRadius: number = null;
|
public windowRoundedCornerRadius: (number | null) = null;
|
||||||
public windowType: string = null;
|
public windowType: (string | null) = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
export default class Track {
|
export default class Track {
|
||||||
public customData: any = null;
|
public customData: any = null;
|
||||||
public language: string = null;
|
public language: (string | null) = null;
|
||||||
public name: string = null;
|
public name: (string | null) = null;
|
||||||
public subtype: string = null;
|
public subtype: (string | null) = null;
|
||||||
public trackContentId: string = null;
|
public trackContentId: (string | null) = null;
|
||||||
public trackContentType: string = null;
|
public trackContentType: (string | null) = null;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public trackId: number
|
public trackId: number
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import { MetadataType } from "../enums";
|
|||||||
|
|
||||||
|
|
||||||
export default class TvShowMediaMetadata {
|
export default class TvShowMediaMetadata {
|
||||||
public episode: number = undefined;
|
public episode: (number | undefined) = undefined;
|
||||||
public episodeNumber: number = undefined;
|
public episodeNumber: (number | undefined) = undefined;
|
||||||
public episodeTitle: string = undefined;
|
public episodeTitle: (string | undefined) = undefined;
|
||||||
public images: Image[] = undefined;
|
public images: (Image[] | undefined) = undefined;
|
||||||
public metadataType: number = MetadataType.TV_SHOW;
|
public metadataType: number = MetadataType.TV_SHOW;
|
||||||
public originalAirdate: string = undefined;
|
public originalAirdate: (string | undefined) = undefined;
|
||||||
public releaseYear: number = undefined;
|
public releaseYear: (number | undefined) = undefined;
|
||||||
public season: number = undefined;
|
public season: (number | undefined) = undefined;
|
||||||
public seasonNumber: number = undefined;
|
public seasonNumber: (number | undefined) = undefined;
|
||||||
public seriesTitle: string = undefined;
|
public seriesTitle: (string | undefined) = undefined;
|
||||||
public title: string = undefined;
|
public title: (string | undefined) = undefined;
|
||||||
public type: number = MetadataType.TV_SHOW;
|
public type: number = MetadataType.TV_SHOW;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { CAST_LOADER_SCRIPT_URL
|
|||||||
* URLs, replace it with the standard API URL, the request for
|
* URLs, replace it with the standard API URL, the request for
|
||||||
* which is handled in the main script.
|
* which is handled in the main script.
|
||||||
*/
|
*/
|
||||||
const { get, set } = Reflect.getOwnPropertyDescriptor(
|
const desc = Reflect.getOwnPropertyDescriptor(
|
||||||
HTMLScriptElement.prototype.wrappedJSObject, "src");
|
HTMLScriptElement.prototype.wrappedJSObject, "src");
|
||||||
|
|
||||||
Reflect.defineProperty(
|
Reflect.defineProperty(
|
||||||
@@ -23,13 +23,13 @@ Reflect.defineProperty(
|
|||||||
|
|
||||||
configurable: true
|
configurable: true
|
||||||
, enumerable: true
|
, enumerable: true
|
||||||
, get
|
, get: desc?.get
|
||||||
|
|
||||||
, set: exportFunction(function (value) {
|
, set: exportFunction(function setFunc (this: HTMLScriptElement, value) {
|
||||||
if (CAST_SCRIPT_URLS.includes(value)) {
|
if (CAST_SCRIPT_URLS.includes(value)) {
|
||||||
return set.call(this, CAST_LOADER_SCRIPT_URL);
|
return desc?.set?.call(this, CAST_LOADER_SCRIPT_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return set.call(this, value);
|
return desc?.set?.call(this, value);
|
||||||
}, window)
|
}, window)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,12 +25,14 @@ export function onMessage (listener: ListenerFunc): ListenerObject {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"__castMessage"
|
"__castMessage"
|
||||||
, on__castMessage, true);
|
, on__castMessage, true);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
disconnect () {
|
disconnect () {
|
||||||
|
// @ts-ignore
|
||||||
document.removeEventListener(
|
document.removeEventListener(
|
||||||
"__castMessage"
|
"__castMessage"
|
||||||
, on__castMessage, true);
|
, on__castMessage, true);
|
||||||
@@ -52,12 +54,14 @@ export function onMessageResponse (listener: ListenerFunc): ListenerObject {
|
|||||||
listener(JSON.parse(ev.detail));
|
listener(JSON.parse(ev.detail));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"__castMessageResponse"
|
"__castMessageResponse"
|
||||||
, on__castMessageResponse, true);
|
, on__castMessageResponse, true);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
disconnect () {
|
disconnect () {
|
||||||
|
// @ts-ignore
|
||||||
document.removeEventListener(
|
document.removeEventListener(
|
||||||
"__castMessageResponse"
|
"__castMessageResponse"
|
||||||
, on__castMessageResponse, true);
|
, on__castMessageResponse, true);
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import * as cast from "../../cast";
|
|||||||
|
|
||||||
export default class CastOptions {
|
export default class CastOptions {
|
||||||
public autoJoinPolicy: string = cast.AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED;
|
public autoJoinPolicy: string = cast.AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED;
|
||||||
public language: string = null;
|
public language: (string | null) = null;
|
||||||
public receiverApplicationId: string = null;
|
public receiverApplicationId: (string | null) = null;
|
||||||
public resumeSavedSession: boolean = true;
|
public resumeSavedSession: boolean = true;
|
||||||
|
|
||||||
constructor (options: CastOptions = ({} as CastOptions)) {
|
constructor (options: CastOptions = ({} as CastOptions)) {
|
||||||
|
|||||||
@@ -15,19 +15,19 @@ export default class RemotePlayer {
|
|||||||
public canControlVolume = false;
|
public canControlVolume = false;
|
||||||
public canPause = false;
|
public canPause = false;
|
||||||
public canSeek = false;
|
public canSeek = false;
|
||||||
public controller: RemotePlayerController = null;
|
public controller: (RemotePlayerController | null) = null;
|
||||||
public currentTime = 0;
|
public currentTime = 0;
|
||||||
public displayName = "";
|
public displayName = "";
|
||||||
public displayStatus = "";
|
public displayStatus = "";
|
||||||
public duration = 0;
|
public duration = 0;
|
||||||
public imageUrl: string = null;
|
public imageUrl: (string | null) = null;
|
||||||
public isConnected = false;
|
public isConnected = false;
|
||||||
public isMediaLoaded = false;
|
public isMediaLoaded = false;
|
||||||
public isMuted = false;
|
public isMuted = false;
|
||||||
public isPaused = false;
|
public isPaused = false;
|
||||||
public mediaInfo: cast.media.MediaInfo = null;
|
public mediaInfo: (cast.media.MediaInfo | null) = null;
|
||||||
public playerState: string = null;
|
public playerState: (string | null) = null;
|
||||||
public savedPlayerState: SavedPlayerState = null;
|
public savedPlayerState: (SavedPlayerState | null) = null;
|
||||||
public statusText = "";
|
public statusText = "";
|
||||||
public title = "";
|
public title = "";
|
||||||
public volumeLevel = 1;
|
public volumeLevel = 1;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default class SessionStateEventData extends EventData {
|
|||||||
constructor (
|
constructor (
|
||||||
public session: CastSession
|
public session: CastSession
|
||||||
, public sessionState: string
|
, public sessionState: string
|
||||||
, public errorCode: string = null) {
|
, public errorCode: (string | null) = null) {
|
||||||
|
|
||||||
super(SessionEventType.APPLICATION_STATUS_CHANGED);
|
super(SessionEventType.APPLICATION_STATUS_CHANGED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ export type MessageListener = (namespace: string, message: string) => void;
|
|||||||
export type UpdateListener = (isAlive: boolean) => void;
|
export type UpdateListener = (isAlive: boolean) => void;
|
||||||
export type LoadSuccessCallback = (media: Media) => void;
|
export type LoadSuccessCallback = (media: Media) => void;
|
||||||
|
|
||||||
export type Callbacks = [ SuccessCallback, ErrorCallback ];
|
export type Callbacks = [ SuccessCallback?, ErrorCallback? ];
|
||||||
export type CallbacksMap = Map<string, Callbacks>;
|
export type CallbacksMap = Map<string, Callbacks>;
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ interface BridgeState {
|
|||||||
isUpdateAvailable: boolean;
|
isUpdateAvailable: boolean;
|
||||||
wasErrorCheckingUpdates: boolean;
|
wasErrorCheckingUpdates: boolean;
|
||||||
checkUpdatesEllipsis: string;
|
checkUpdatesEllipsis: string;
|
||||||
updateStatus: string;
|
updateStatus?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Bridge extends Component<BridgeProps, BridgeState> {
|
export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||||
private updateData: any;
|
private updateData: any;
|
||||||
private updateStatusTimeout: number;
|
private updateStatusTimeout?: number;
|
||||||
|
|
||||||
constructor (props: BridgeProps) {
|
constructor (props: BridgeProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -83,7 +83,6 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
|||||||
, isUpdateAvailable: false
|
, isUpdateAvailable: false
|
||||||
, wasErrorCheckingUpdates: false
|
, wasErrorCheckingUpdates: false
|
||||||
, checkUpdatesEllipsis: "..."
|
, checkUpdatesEllipsis: "..."
|
||||||
, updateStatus: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onCheckUpdates = this.onCheckUpdates.bind(this);
|
this.onCheckUpdates = this.onCheckUpdates.bind(this);
|
||||||
@@ -142,7 +141,7 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
|||||||
|
|
||||||
let statusIcon: string;
|
let statusIcon: string;
|
||||||
let statusTitle: string;
|
let statusTitle: string;
|
||||||
let statusText: string;
|
let statusText: (string | null);
|
||||||
|
|
||||||
if (!this.props.info) {
|
if (!this.props.info) {
|
||||||
statusIcon = "assets/icons8-cancel-120.png";
|
statusIcon = "assets/icons8-cancel-120.png";
|
||||||
@@ -156,6 +155,8 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
|||||||
statusIcon = "assets/icons8-warn-120.png";
|
statusIcon = "assets/icons8-warn-120.png";
|
||||||
statusTitle = _("optionsBridgeIssueStatusTitle");
|
statusTitle = _("optionsBridgeIssueStatusTitle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statusText = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -213,11 +214,14 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
isCheckingUpdates: false
|
isCheckingUpdates: false
|
||||||
, isUpdateAvailable
|
, isUpdateAvailable
|
||||||
, updateStatus: !isUpdateAvailable
|
|
||||||
? _("optionsBridgeUpdateStatusNoUpdates")
|
|
||||||
: null
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!isUpdateAvailable) {
|
||||||
|
this.setState({
|
||||||
|
updateStatus: _("optionsBridgeUpdateStatusNoUpdates")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.showUpdateStatus();
|
this.showUpdateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +241,7 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
|||||||
}
|
}
|
||||||
this.updateStatusTimeout = window.setTimeout(() => {
|
this.updateStatusTimeout = window.setTimeout(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
updateStatus: null
|
updateStatus: undefined
|
||||||
});
|
});
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface EditableListState {
|
|||||||
export default class EditableList extends Component<
|
export default class EditableList extends Component<
|
||||||
EditableListProps, EditableListState> {
|
EditableListProps, EditableListState> {
|
||||||
|
|
||||||
private rawViewTextArea: HTMLTextAreaElement;
|
private rawViewTextArea: (HTMLTextAreaElement | null) = null;
|
||||||
|
|
||||||
constructor (props: EditableListProps) {
|
constructor (props: EditableListProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -140,13 +140,13 @@ export default class EditableList extends Component<
|
|||||||
if ("itemPattern" in this.props) {
|
if ("itemPattern" in this.props) {
|
||||||
for (const item of newItems) {
|
for (const item of newItems) {
|
||||||
if (!this.props.itemPattern.test(item)) {
|
if (!this.props.itemPattern.test(item)) {
|
||||||
this.rawViewTextArea.setCustomValidity(
|
this.rawViewTextArea?.setCustomValidity(
|
||||||
this.props.itemPatternError(item));
|
this.props.itemPatternError(item));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rawViewTextArea.setCustomValidity("");
|
this.rawViewTextArea?.setCustomValidity("");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onChange(newItems);
|
this.props.onChange(newItems);
|
||||||
@@ -154,6 +154,10 @@ export default class EditableList extends Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleRawViewTextAreaChange (ev: React.ChangeEvent<HTMLTextAreaElement>) {
|
private handleRawViewTextAreaChange (ev: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||||
|
if (!this.rawViewTextArea) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
|
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
|
||||||
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
|
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface EditableListItemState {
|
|||||||
export default class EditableListItem extends Component<
|
export default class EditableListItem extends Component<
|
||||||
EditableListItemProps, EditableListItemState> {
|
EditableListItemProps, EditableListItemState> {
|
||||||
|
|
||||||
private input: HTMLInputElement;
|
private input: (HTMLInputElement | null) = null;
|
||||||
|
|
||||||
constructor (props: EditableListItemProps) {
|
constructor (props: EditableListItemProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -96,7 +96,7 @@ export default class EditableListItem extends Component<
|
|||||||
editing: true
|
editing: true
|
||||||
, editValue: this.props.text
|
, editValue: this.props.text
|
||||||
}, () => {
|
}, () => {
|
||||||
this.input.focus();
|
this.input?.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import Bridge from "./Bridge";
|
|||||||
import EditableList from "./EditableList";
|
import EditableList from "./EditableList";
|
||||||
|
|
||||||
import bridge, { BridgeInfo } from "../../lib/bridge";
|
import bridge, { BridgeInfo } from "../../lib/bridge";
|
||||||
|
import logger from "../../lib/logger";
|
||||||
import options, { Options } from "../../lib/options";
|
import options, { Options } from "../../lib/options";
|
||||||
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils";
|
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils";
|
||||||
|
|
||||||
@@ -83,25 +84,23 @@ function getInputValue (input: HTMLInputElement) {
|
|||||||
|
|
||||||
interface OptionsAppState {
|
interface OptionsAppState {
|
||||||
hasLoaded: boolean;
|
hasLoaded: boolean;
|
||||||
options: Options;
|
|
||||||
bridgeInfo: BridgeInfo;
|
|
||||||
platform: string;
|
|
||||||
bridgeLoading: boolean;
|
bridgeLoading: boolean;
|
||||||
isFormValid: boolean;
|
isFormValid: boolean;
|
||||||
hasSaved: boolean;
|
hasSaved: boolean;
|
||||||
|
|
||||||
|
options?: Options;
|
||||||
|
bridgeInfo?: BridgeInfo;
|
||||||
|
platform?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class OptionsApp extends Component<{}, OptionsAppState> {
|
class OptionsApp extends Component<{}, OptionsAppState> {
|
||||||
private form: HTMLFormElement;
|
private form: (HTMLFormElement | null) = null;
|
||||||
|
|
||||||
constructor (props: {}) {
|
constructor (props: {}) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hasLoaded: false
|
hasLoaded: false
|
||||||
, options: null
|
|
||||||
, bridgeInfo: null
|
|
||||||
, platform: null
|
|
||||||
, bridgeLoading: true
|
, bridgeLoading: true
|
||||||
, isFormValid: true
|
, isFormValid: true
|
||||||
, hasSaved: false
|
, hasSaved: false
|
||||||
@@ -113,11 +112,11 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
this.handleWhitelistChange = this.handleWhitelistChange.bind(this);
|
this.handleWhitelistChange = this.handleWhitelistChange.bind(this);
|
||||||
|
|
||||||
this.handleReceiverSelectorTypeChange
|
this.handleReceiverSelectorTypeChange =
|
||||||
= this.handleReceiverSelectorTypeChange.bind(this);
|
this.handleReceiverSelectorTypeChange.bind(this);
|
||||||
|
|
||||||
this.getWhitelistItemPatternError
|
this.getWhitelistItemPatternError =
|
||||||
= this.getWhitelistItemPatternError.bind(this);
|
this.getWhitelistItemPatternError.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidMount () {
|
public async componentDidMount () {
|
||||||
@@ -126,14 +125,18 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
, options: await options.getAll()
|
, options: await options.getAll()
|
||||||
});
|
});
|
||||||
|
|
||||||
const bridgeInfo = await bridge.getInfo();
|
try {
|
||||||
const { os } = await browser.runtime.getPlatformInfo();
|
const bridgeInfo = await bridge.getInfo();
|
||||||
|
const { os } = await browser.runtime.getPlatformInfo();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
bridgeInfo
|
bridgeInfo
|
||||||
, platform: os
|
, platform: os
|
||||||
, bridgeLoading: false
|
, bridgeLoading: false
|
||||||
});
|
});
|
||||||
|
} catch {
|
||||||
|
logger.error("Failed to fetch bridge/platform info.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render () {
|
public render () {
|
||||||
@@ -143,9 +146,10 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Bridge info={ this.state.bridgeInfo }
|
{ this.state.bridgeInfo && this.state.platform &&
|
||||||
platform={ this.state.platform }
|
<Bridge info={ this.state.bridgeInfo }
|
||||||
loading={ this.state.bridgeLoading } />
|
platform={ this.state.platform }
|
||||||
|
loading={ this.state.bridgeLoading } /> }
|
||||||
|
|
||||||
<form id="form" ref={ form => { this.form = form; }}
|
<form id="form" ref={ form => { this.form = form; }}
|
||||||
onSubmit={ this.handleFormSubmit }
|
onSubmit={ this.handleFormSubmit }
|
||||||
@@ -163,7 +167,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="mediaEnabled"
|
<input name="mediaEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.mediaEnabled }
|
checked={ this.state.options?.mediaEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -175,7 +179,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="mediaSyncElement"
|
<input name="mediaSyncElement"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.mediaSyncElement }
|
checked={ this.state.options?.mediaSyncElement }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -190,7 +194,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="mediaStopOnUnload"
|
<input name="mediaStopOnUnload"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.mediaStopOnUnload }
|
checked={ this.state.options?.mediaStopOnUnload }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -202,7 +206,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="mediaOverlayEnabled"
|
<input name="mediaOverlayEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.mediaOverlayEnabled }
|
checked={ this.state.options?.mediaOverlayEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -219,7 +223,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="localMediaEnabled"
|
<input name="localMediaEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.localMediaEnabled }
|
checked={ this.state.options?.localMediaEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -240,7 +244,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
required
|
required
|
||||||
min="1025"
|
min="1025"
|
||||||
max="65535"
|
max="65535"
|
||||||
value={ this.state.options.localMediaServerPort }
|
value={ this.state.options?.localMediaServerPort }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
@@ -258,7 +262,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="mirroringEnabled"
|
<input name="mirroringEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.mirroringEnabled }
|
checked={ this.state.options?.mirroringEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -274,7 +278,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<input name="mirroringAppId"
|
<input name="mirroringAppId"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
value={ this.state.options.mirroringAppId }
|
value={ this.state.options?.mirroringAppId }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
<div className="option__description">
|
<div className="option__description">
|
||||||
{ _("optionsMirroringAppIdDescription") }
|
{ _("optionsMirroringAppIdDescription") }
|
||||||
@@ -298,7 +302,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<select name="receiverSelectorType"
|
<select name="receiverSelectorType"
|
||||||
value={ this.state.options.receiverSelectorType }
|
value={ this.state.options?.receiverSelectorType }
|
||||||
onChange={ this.handleReceiverSelectorTypeChange }>
|
onChange={ this.handleReceiverSelectorTypeChange }>
|
||||||
<option value={ ReceiverSelectorType.Popup }>
|
<option value={ ReceiverSelectorType.Popup }>
|
||||||
{ _("optionsReceiverSelectorTypeBrowser") }
|
{ _("optionsReceiverSelectorTypeBrowser") }
|
||||||
@@ -314,7 +318,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="receiverSelectorWaitForConnection"
|
<input name="receiverSelectorWaitForConnection"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.receiverSelectorWaitForConnection }
|
checked={ this.state.options?.receiverSelectorWaitForConnection }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -329,7 +333,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="receiverSelectorCloseIfFocusLost"
|
<input name="receiverSelectorCloseIfFocusLost"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.receiverSelectorCloseIfFocusLost }
|
checked={ this.state.options?.receiverSelectorCloseIfFocusLost }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -350,7 +354,7 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<input name="userAgentWhitelistEnabled"
|
<input name="userAgentWhitelistEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.options.userAgentWhitelistEnabled }
|
checked={ this.state.options?.userAgentWhitelistEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</div>
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
@@ -363,10 +367,11 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
{ _("optionsUserAgentWhitelistContent") }
|
{ _("optionsUserAgentWhitelistContent") }
|
||||||
</div>
|
</div>
|
||||||
<div className="option__control">
|
<div className="option__control">
|
||||||
<EditableList data={ this.state.options.userAgentWhitelist }
|
{ this.state.options?.userAgentWhitelist &&
|
||||||
onChange={ this.handleWhitelistChange }
|
<EditableList data={ this.state.options.userAgentWhitelist }
|
||||||
itemPattern={ REMOTE_MATCH_PATTERN_REGEX }
|
onChange={ this.handleWhitelistChange }
|
||||||
itemPatternError={ this.getWhitelistItemPatternError } />
|
itemPattern={ REMOTE_MATCH_PATTERN_REGEX }
|
||||||
|
itemPatternError={ this.getWhitelistItemPatternError } /> }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -400,20 +405,22 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
private async handleFormSubmit (ev: React.FormEvent<HTMLFormElement>) {
|
private async handleFormSubmit (ev: React.FormEvent<HTMLFormElement>) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
this.form.reportValidity();
|
this.form?.reportValidity();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await options.setAll(this.state.options);
|
if (this.state.options) {
|
||||||
|
await options.setAll(this.state.options);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
hasSaved: true
|
hasSaved: true
|
||||||
}, () => {
|
}, () => {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
hasSaved: false
|
hasSaved: false
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to save options");
|
console.error("Failed to save options");
|
||||||
}
|
}
|
||||||
@@ -422,14 +429,20 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
private handleFormChange (ev: React.FormEvent<HTMLFormElement>) {
|
private handleFormChange (ev: React.FormEvent<HTMLFormElement>) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
this.setState({
|
const isFormValid = this.form?.checkValidity();
|
||||||
isFormValid: this.form.checkValidity()
|
if (isFormValid !== undefined) {
|
||||||
});
|
this.setState({
|
||||||
|
isFormValid
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleInputChange (ev: React.ChangeEvent<HTMLInputElement>) {
|
private handleInputChange (ev: React.ChangeEvent<HTMLInputElement>) {
|
||||||
this.setState(currentState => {
|
this.setState(currentState => {
|
||||||
currentState.options[ev.target.name] = getInputValue(ev.target);
|
if (currentState.options) {
|
||||||
|
currentState.options[ev.target.name] = getInputValue(ev.target);
|
||||||
|
}
|
||||||
|
|
||||||
return currentState;
|
return currentState;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -438,14 +451,20 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
ev: React.ChangeEvent<HTMLSelectElement>) {
|
ev: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
|
||||||
this.setState(currentState => {
|
this.setState(currentState => {
|
||||||
currentState.options[ev.target.name] = parseInt(ev.target.value);
|
if (currentState.options) {
|
||||||
|
currentState.options[ev.target.name] = parseInt(ev.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
return currentState;
|
return currentState;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleWhitelistChange (whitelist: string[]) {
|
private handleWhitelistChange (whitelist: string[]) {
|
||||||
this.setState(currentState => {
|
this.setState(currentState => {
|
||||||
currentState.options.userAgentWhitelist = whitelist;
|
if (currentState.options) {
|
||||||
|
currentState.options.userAgentWhitelist = whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
return currentState;
|
return currentState;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,14 +31,15 @@ interface PopupAppState {
|
|||||||
mediaType: ReceiverSelectorMediaType;
|
mediaType: ReceiverSelectorMediaType;
|
||||||
availableMediaTypes: ReceiverSelectorMediaType;
|
availableMediaTypes: ReceiverSelectorMediaType;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
filePath: string;
|
|
||||||
requestedAppId: string;
|
filePath?: string;
|
||||||
|
requestedAppId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PopupApp extends Component<{}, PopupAppState> {
|
class PopupApp extends Component<{}, PopupAppState> {
|
||||||
private port: browser.runtime.Port;
|
private port?: browser.runtime.Port;
|
||||||
private win: browser.windows.Window;
|
private win?: browser.windows.Window;
|
||||||
private defaultMediaType: ReceiverSelectorMediaType;
|
private defaultMediaType?: ReceiverSelectorMediaType;
|
||||||
|
|
||||||
constructor (props: {}) {
|
constructor (props: {}) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -48,8 +49,6 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
, mediaType: ReceiverSelectorMediaType.App
|
, mediaType: ReceiverSelectorMediaType.App
|
||||||
, availableMediaTypes: ReceiverSelectorMediaType.App
|
, availableMediaTypes: ReceiverSelectorMediaType.App
|
||||||
, isLoading: false
|
, isLoading: false
|
||||||
, filePath: null
|
|
||||||
, requestedAppId: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store window ref
|
// Store window ref
|
||||||
@@ -78,7 +77,12 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "popup:/populateReceiverList": {
|
case "popup:/populateReceiverList": {
|
||||||
this.defaultMediaType = message.data.defaultMediaType;
|
const { receivers, availableMediaTypes, defaultMediaType }
|
||||||
|
: { receivers: Receiver[]
|
||||||
|
, availableMediaTypes: ReceiverSelectorMediaType
|
||||||
|
, defaultMediaType: ReceiverSelectorMediaType } = message.data;
|
||||||
|
|
||||||
|
this.defaultMediaType = defaultMediaType;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
receivers: message.data.receivers
|
receivers: message.data.receivers
|
||||||
@@ -99,6 +103,10 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
|
|
||||||
public componentDidUpdate () {
|
public componentDidUpdate () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (this.win?.id === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Fit window to content height
|
// Fit window to content height
|
||||||
const frameHeight = window.outerHeight - window.innerHeight;
|
const frameHeight = window.outerHeight - window.innerHeight;
|
||||||
const windowHeight = document.body.clientHeight + frameHeight;
|
const windowHeight = document.body.clientHeight + frameHeight;
|
||||||
@@ -137,8 +145,9 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
<option value={ ReceiverSelectorMediaType.App }
|
<option value={ ReceiverSelectorMediaType.App }
|
||||||
disabled={ !(this.state.availableMediaTypes
|
disabled={ !(this.state.availableMediaTypes
|
||||||
& ReceiverSelectorMediaType.App) }>
|
& ReceiverSelectorMediaType.App) }>
|
||||||
{ knownApps[this.state.requestedAppId]
|
{ (this.state.requestedAppId
|
||||||
?? _("popupMediaTypeApp") }
|
&& knownApps[this.state.requestedAppId])
|
||||||
|
?? _("popupMediaTypeApp") }
|
||||||
</option>
|
</option>
|
||||||
<option value={ ReceiverSelectorMediaType.Tab }
|
<option value={ ReceiverSelectorMediaType.Tab }
|
||||||
disabled={ !(this.state.availableMediaTypes
|
disabled={ !(this.state.availableMediaTypes
|
||||||
@@ -157,7 +166,7 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
disabled={ !(this.state.availableMediaTypes
|
disabled={ !(this.state.availableMediaTypes
|
||||||
& ReceiverSelectorMediaType.File) }>
|
& ReceiverSelectorMediaType.File) }>
|
||||||
{ this.state.filePath
|
{ this.state.filePath
|
||||||
? truncatedFileName
|
? truncatedFileName!
|
||||||
: _("popupMediaTypeFile") }
|
: _("popupMediaTypeFile") }
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -188,7 +197,7 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
isLoading: true
|
isLoading: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.port.postMessage({
|
this.port?.postMessage({
|
||||||
subject: "receiverSelector:/selected"
|
subject: "receiverSelector:/selected"
|
||||||
, data: {
|
, data: {
|
||||||
receiver
|
receiver
|
||||||
@@ -199,7 +208,7 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onStop (receiver: Receiver) {
|
private onStop (receiver: Receiver) {
|
||||||
this.port.postMessage({
|
this.port?.postMessage({
|
||||||
subject: "receiverSelector:/stop"
|
subject: "receiverSelector:/stop"
|
||||||
, data: { receiver }
|
, data: { receiver }
|
||||||
});
|
});
|
||||||
@@ -220,9 +229,11 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set media type to default if failed to set filePath
|
// Set media type to default if failed to set filePath
|
||||||
this.setState({
|
if (this.defaultMediaType) {
|
||||||
mediaType: this.defaultMediaType
|
this.setState({
|
||||||
});
|
mediaType: this.defaultMediaType
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
mediaType
|
mediaType
|
||||||
@@ -230,7 +241,7 @@ class PopupApp extends Component<{}, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
filePath: null
|
filePath: undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,6 +290,10 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render () {
|
public render () {
|
||||||
|
if (!this.props.receiver.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { application } = this.props.receiver.status;
|
const { application } = this.props.receiver.status;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -287,7 +302,7 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
|||||||
{ this.props.receiver.friendlyName }
|
{ this.props.receiver.friendlyName }
|
||||||
</div>
|
</div>
|
||||||
<div className="receiver__address"
|
<div className="receiver__address"
|
||||||
title={ !application.isIdleScreen && application.statusText }>
|
title={ !application.isIdleScreen ? application.statusText : "" }>
|
||||||
{ application.isIdleScreen
|
{ application.isIdleScreen
|
||||||
? `${this.props.receiver.host}:${this.props.receiver.port}`
|
? `${this.props.receiver.host}:${this.props.receiver.port}`
|
||||||
: application.statusText }
|
: application.statusText }
|
||||||
@@ -311,6 +326,10 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleCast () {
|
private handleCast () {
|
||||||
|
if (!this.props.receiver.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { application } = this.props.receiver.status;
|
const { application } = this.props.receiver.status;
|
||||||
|
|
||||||
if (!application.isIdleScreen && this.state.showAlternateAction) {
|
if (!application.isIdleScreen && this.state.showAlternateAction) {
|
||||||
|
|||||||
@@ -7,5 +7,6 @@
|
|||||||
, "removeComments": true
|
, "removeComments": true
|
||||||
, "resolveJsonModule": true
|
, "resolveJsonModule": true
|
||||||
, "target": "es6"
|
, "target": "es6"
|
||||||
|
. "strict": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user