mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Narrow linting rules and fix for eslintrc for js files
This commit is contained in:
@@ -1,30 +1,48 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [ "@typescript-eslint" ],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": ["eslint:recommended", "prettier"],
|
||||
"rules": {
|
||||
"no-useless-escape": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-async-promise-executor": "off",
|
||||
"semi": [ "error", "always"],
|
||||
"no-multiple-empty-lines": [ "error", { "max": 2 }],
|
||||
"no-console": [ "error", {
|
||||
"allow": [ "info", "warn", "error" ]
|
||||
}],
|
||||
"semi": ["error", "always"],
|
||||
"no-multiple-empty-lines": ["error", { "max": 2 }],
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": ["info", "warn", "error"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-this-alias": [ "error", {
|
||||
"allowedNames": [ "this_" ]
|
||||
}]
|
||||
}
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*/bin/**/*.js"],
|
||||
"env": {
|
||||
"node": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["**/*.ts", "**/*.tsx"],
|
||||
"extends": "plugin:@typescript-eslint/recommended",
|
||||
"rules": {
|
||||
"@typescript-eslint/no-empty-interface": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{ "argsIgnorePattern": "^_" }
|
||||
],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-this-alias": [
|
||||
"error",
|
||||
{
|
||||
"allowedNames": ["this_"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -489,8 +489,7 @@ function packageLinuxRpm(
|
||||
function packageWin32(
|
||||
arch,
|
||||
platformExecutableName,
|
||||
platformExecutablePath,
|
||||
platformManifestPath
|
||||
platformExecutablePath
|
||||
) {
|
||||
const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.exe`;
|
||||
|
||||
@@ -525,6 +524,6 @@ function packageWin32(
|
||||
}
|
||||
|
||||
build().catch(e => {
|
||||
console.log("Build failed", e);
|
||||
console.error("Build failed", e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
1
app/package-lock.json
generated
1
app/package-lock.json
generated
@@ -13,7 +13,6 @@
|
||||
"mime-types": "^2.1.35",
|
||||
"minimist": "^1.2.6",
|
||||
"node-fetch": "^3.2.3",
|
||||
"rage-edit": "*",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
|
||||
@@ -72,8 +72,6 @@ export default class Session extends CastClient {
|
||||
this.establishAppConnection(this.transportId);
|
||||
this.onSessionCreated?.(this.sessionId);
|
||||
|
||||
const { friendlyName } = this.receiverDevice;
|
||||
|
||||
messaging.sendMessage({
|
||||
subject: "cast:sessionCreated",
|
||||
data: {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import messaging, { Message } from "../../messaging";
|
||||
|
||||
import Session from "./Session";
|
||||
import CastClient, { NS_CONNECTION, NS_RECEIVER } from "./client";
|
||||
import CastClient from "./client";
|
||||
|
||||
const sessions = new Map<string, Session>();
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ interface BreakClip {
|
||||
contentId?: string;
|
||||
contentType?: string;
|
||||
contentUrl?: string;
|
||||
customData?: {};
|
||||
customData?: unknown;
|
||||
duration?: number;
|
||||
id: string;
|
||||
hlsSegmentFormat?: HlsSegmentFormat;
|
||||
@@ -137,7 +137,7 @@ interface BreakClip {
|
||||
|
||||
interface TextTrackStyle {
|
||||
backgroundColor: Nullable<string>;
|
||||
customData: any;
|
||||
customData: unknown;
|
||||
edgeColor: Nullable<string>;
|
||||
edgeType: Nullable<string>;
|
||||
fontFamily: Nullable<string>;
|
||||
@@ -151,7 +151,7 @@ interface TextTrackStyle {
|
||||
}
|
||||
|
||||
interface Track {
|
||||
customData: any;
|
||||
customData: unknown;
|
||||
language: Nullable<string>;
|
||||
name: Nullable<string>;
|
||||
subtype: Nullable<string>;
|
||||
@@ -162,7 +162,7 @@ interface Track {
|
||||
}
|
||||
|
||||
interface UserActionState {
|
||||
customData: any;
|
||||
customData: unknown;
|
||||
userAction: UserAction;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ interface MediaInformation {
|
||||
contentId: string;
|
||||
contentType: string;
|
||||
contentUrl?: string;
|
||||
customData: any;
|
||||
customData: unknown;
|
||||
duration: Nullable<number>;
|
||||
entity?: string;
|
||||
hlsSegmentFormat?: HlsSegmentFormat;
|
||||
@@ -269,7 +269,7 @@ interface PhotoMediaMetadata {
|
||||
interface QueueItem {
|
||||
activeTrackIds: Nullable<number[]>;
|
||||
autoplay: boolean;
|
||||
customData: any;
|
||||
customData: unknown;
|
||||
itemId: Nullable<number>;
|
||||
media: MediaInformation;
|
||||
playbackDuration: Nullable<number>;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { handleCastMessage } from "./components/cast";
|
||||
import { startDiscovery, stopDiscovery } from "./components/discovery";
|
||||
import { startMediaServer, stopMediaServer } from "./components/mediaServer";
|
||||
|
||||
import { __applicationName, __applicationVersion } from "../../package.json";
|
||||
import { __applicationVersion } from "../../package.json";
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
stopDiscovery();
|
||||
|
||||
@@ -37,7 +37,6 @@ export async function convertSrtToVtt(srtFilePath: string) {
|
||||
* millisecond separator.
|
||||
*/
|
||||
for (const groups of fileContents.matchAll(REGEX_CAPTION)) {
|
||||
const captionSource = groups[0];
|
||||
const captionIndex = groups[1];
|
||||
const captionTime = groups[2];
|
||||
const captionText = groups[3];
|
||||
|
||||
@@ -163,12 +163,12 @@ type MessageDefinitions = {
|
||||
/**
|
||||
* Sent to bridge to stop HTTP media server.
|
||||
*/
|
||||
"bridge:stopMediaServer": {};
|
||||
"bridge:stopMediaServer": undefined;
|
||||
/**
|
||||
* Sent to media sender from bridge when the media server has
|
||||
* stopped.
|
||||
*/
|
||||
"mediaCast:mediaServerStopped": {};
|
||||
"mediaCast:mediaServerStopped": undefined;
|
||||
/**
|
||||
* Sent to media sender from bridge when the media server has
|
||||
* encountered an error.
|
||||
@@ -190,8 +190,8 @@ type Messages = {
|
||||
* all-optional keys.
|
||||
*/
|
||||
type NarrowedMessage<L extends MessageBase<keyof MessageDefinitions>> =
|
||||
L extends any
|
||||
? {} extends L["data"]
|
||||
L extends unknown
|
||||
? undefined extends L["data"]
|
||||
? Omit<L, "data"> & Partial<L>
|
||||
: L
|
||||
: never;
|
||||
@@ -231,7 +231,7 @@ class Messenger extends TypedEmitter<MessengerEvents> {
|
||||
this.encodeTransform.write(message);
|
||||
}
|
||||
|
||||
send(data: any) {
|
||||
send(data: unknown) {
|
||||
this.encodeTransform.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
import { Transform } from "stream";
|
||||
import { Transform, TransformCallback } from "stream";
|
||||
import { Message } from "./bridge/messaging";
|
||||
|
||||
type ResponseHandlerFunction = (message: Message) => Promise<any>;
|
||||
type ResponseHandlerFunction = (message: Message) => Promise<unknown>;
|
||||
|
||||
/**
|
||||
* Takes a handler function that implements the transform
|
||||
@@ -20,8 +20,7 @@ export class ResponseTransform extends Transform {
|
||||
public _transform(
|
||||
chunk: Message,
|
||||
_encoding: string,
|
||||
// tslint:disable-next-line:ban-types
|
||||
callback: Function
|
||||
callback: TransformCallback
|
||||
) {
|
||||
Promise.resolve(this._handler(chunk)).then(res => {
|
||||
if (res) {
|
||||
@@ -49,10 +48,10 @@ export class DecodeTransform extends Transform {
|
||||
}
|
||||
|
||||
public _transform(
|
||||
chunk: any,
|
||||
chunk: Uint8Array,
|
||||
_encoding: string,
|
||||
// tslint:disable-next-line:ban-types
|
||||
callback: Function
|
||||
callback: TransformCallback
|
||||
) {
|
||||
// Append next chunk to buffer
|
||||
this._messageBuffer = Buffer.concat([this._messageBuffer, chunk]);
|
||||
@@ -108,10 +107,10 @@ export class EncodeTransform extends Transform {
|
||||
}
|
||||
|
||||
public _transform(
|
||||
chunk: any,
|
||||
chunk: Uint8Array,
|
||||
_encoding: string,
|
||||
// tslint:disable-next-line:ban-types
|
||||
callback: Function
|
||||
callback: TransformCallback
|
||||
) {
|
||||
const messageLength = Buffer.alloc(4);
|
||||
const message = Buffer.from(JSON.stringify(chunk));
|
||||
|
||||
@@ -53,7 +53,7 @@ const preactCompatPlugin = {
|
||||
"../node_modules/preact/compat/dist/compat.module.js"
|
||||
);
|
||||
|
||||
build.onResolve({ filter: /^(react|react-dom)$/ }, args => ({
|
||||
build.onResolve({ filter: /^(react|react-dom)$/ }, () => ({
|
||||
path: preactPath
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import logger from "../lib/logger";
|
||||
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||
|
||||
import { Message, Port } from "../messaging";
|
||||
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
|
||||
import { ReceiverDevice } from "../types";
|
||||
import { ReceiverStatus } from "../cast/sdk/types";
|
||||
|
||||
interface EventMap {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
import logger from "../lib/logger";
|
||||
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||
import { Message } from "../messaging";
|
||||
|
||||
type EventMessengerListener = (message: Message) => void;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
import messaging, { Message } from "../messaging";
|
||||
import { Message } from "../messaging";
|
||||
|
||||
import { BridgeInfo } from "../lib/bridge";
|
||||
import { TypedMessagePort } from "../lib/TypedMessagePort";
|
||||
|
||||
@@ -17,8 +17,7 @@ if (!_window.chrome) {
|
||||
// Create page-accessible API object
|
||||
_window.chrome.cast = new CastSDK();
|
||||
|
||||
let bridgeInfo: any;
|
||||
let frameworkScriptPromise: Promise<HTMLScriptElement>;
|
||||
let frameworkScriptPromise: Promise<HTMLScriptElement> | undefined;
|
||||
|
||||
/**
|
||||
* If loaded within a page via a <script> element,
|
||||
@@ -27,8 +26,9 @@ let frameworkScriptPromise: Promise<HTMLScriptElement>;
|
||||
*/
|
||||
if (document.currentScript) {
|
||||
const currentScript = document.currentScript as HTMLScriptElement;
|
||||
const currentScriptUrl = new URL(currentScript.src);
|
||||
const currentScriptParams = new URLSearchParams(currentScriptUrl.search);
|
||||
const currentScriptParams = new URLSearchParams(
|
||||
new URL(currentScript.src).search
|
||||
);
|
||||
|
||||
// Load Framework API if requested
|
||||
if (currentScriptParams.get("loadCastFramework") === "1") {
|
||||
@@ -43,17 +43,13 @@ if (document.currentScript) {
|
||||
eventMessaging.page.addListener(async message => {
|
||||
switch (message.subject) {
|
||||
case "cast:initialized": {
|
||||
bridgeInfo = message.data;
|
||||
|
||||
// If framework API is requested, ensure loaded
|
||||
if (frameworkScriptPromise) {
|
||||
await frameworkScriptPromise;
|
||||
}
|
||||
await frameworkScriptPromise;
|
||||
|
||||
// Call page script/framework API script's init function
|
||||
const initFn = _window.__onGCastApiAvailable;
|
||||
if (initFn && typeof initFn === "function") {
|
||||
initFn(bridgeInfo && bridgeInfo.isVersionCompatible);
|
||||
initFn(message.data.isVersionCompatible);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -36,7 +36,7 @@ export class Error {
|
||||
constructor(
|
||||
public code: string,
|
||||
public description: Nullable<string> = null,
|
||||
public details: any = null
|
||||
public details: unknown = null
|
||||
) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class Media {
|
||||
activeTrackIds: Nullable<number[]> = null;
|
||||
breakStatus?: BreakStatus;
|
||||
currentTime = 0;
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
idleReason: Nullable<string> = null;
|
||||
liveSeekableRange?: LiveSeekableRange;
|
||||
media: Nullable<MediaInfo> = null;
|
||||
|
||||
@@ -49,7 +49,7 @@ export class BreakClip {
|
||||
contentId?: string;
|
||||
contentType?: string;
|
||||
contentUrl?: string;
|
||||
customData?: {};
|
||||
customData?: unknown;
|
||||
duration?: number;
|
||||
hlsSegmentFormat?: HlsSegmentFormat;
|
||||
posterUrl?: string;
|
||||
@@ -99,7 +99,7 @@ export class GenericMediaMetadata {
|
||||
}
|
||||
|
||||
export class GetStatusRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
}
|
||||
|
||||
export class LiveSeekableRange {
|
||||
@@ -117,7 +117,7 @@ export class LoadRequest {
|
||||
atvCredentialsType?: string;
|
||||
autoplay: Nullable<boolean> = true;
|
||||
currentTime: Nullable<number> = null;
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
media: MediaInfo;
|
||||
requestId = 0;
|
||||
sessionId: Nullable<string> = null;
|
||||
@@ -139,7 +139,7 @@ export class MediaInfo {
|
||||
atvEntity?: string;
|
||||
breakClips?: BreakClip[];
|
||||
breaks?: Break[];
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
contentUrl?: string;
|
||||
duration: Nullable<number> = null;
|
||||
entity?: string;
|
||||
@@ -200,7 +200,7 @@ export class MusicTrackMediaMetadata {
|
||||
}
|
||||
|
||||
export class PauseRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
}
|
||||
|
||||
export class PhotoMediaMetadata {
|
||||
@@ -218,7 +218,7 @@ export class PhotoMediaMetadata {
|
||||
}
|
||||
|
||||
export class PlayRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
}
|
||||
|
||||
export class QueueData {
|
||||
@@ -236,7 +236,7 @@ export class QueueData {
|
||||
}
|
||||
|
||||
export class QueueInsertItemsRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
insertBefore: Nullable<number> = null;
|
||||
requestId: Nullable<number> = null;
|
||||
sessionId: Nullable<string> = null;
|
||||
@@ -248,7 +248,7 @@ export class QueueInsertItemsRequest {
|
||||
export class QueueItem {
|
||||
activeTrackIds: Nullable<number[]> = null;
|
||||
autoplay = true;
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
itemId: Nullable<number> = null;
|
||||
media: MediaInfo;
|
||||
playbackDuration: Nullable<number> = null;
|
||||
@@ -268,7 +268,7 @@ export class QueueJumpRequest {
|
||||
|
||||
export class QueueLoadRequest {
|
||||
type = "QUEUE_LOAD";
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
repeatMode: string = RepeatMode.OFF;
|
||||
startIndex = 0;
|
||||
|
||||
@@ -277,13 +277,13 @@ export class QueueLoadRequest {
|
||||
|
||||
export class QueueRemoveItemsRequest {
|
||||
type = "QUEUE_REMOVE";
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
|
||||
constructor(public itemIds: number[]) {}
|
||||
}
|
||||
|
||||
export class QueueReorderItemsRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
insertBefore: Nullable<number> = null;
|
||||
type = "QUEUE_REORDER";
|
||||
|
||||
@@ -292,30 +292,30 @@ export class QueueReorderItemsRequest {
|
||||
|
||||
export class QueueSetPropertiesRequest {
|
||||
type = "QUEUE_UPDATE";
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
repeatMode: Nullable<string> = null;
|
||||
}
|
||||
|
||||
export class QueueUpdateItemsRequest {
|
||||
type = "QUEUE_UPDATE";
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
|
||||
constructor(public items: QueueItem[]) {}
|
||||
}
|
||||
|
||||
export class SeekRequest {
|
||||
currentTime: Nullable<number> = null;
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
resumeState: Nullable<ResumeState> = null;
|
||||
}
|
||||
|
||||
export class StopRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
}
|
||||
|
||||
export class TextTrackStyle {
|
||||
backgroundColor: Nullable<string> = null;
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
edgeColor: Nullable<string> = null;
|
||||
edgeType: Nullable<string> = null;
|
||||
fontFamily: Nullable<string> = null;
|
||||
@@ -329,7 +329,7 @@ export class TextTrackStyle {
|
||||
}
|
||||
|
||||
export class Track {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
language: Nullable<string> = null;
|
||||
name: Nullable<string> = null;
|
||||
subtype: Nullable<string> = null;
|
||||
@@ -355,7 +355,7 @@ export class TvShowMediaMetadata {
|
||||
}
|
||||
|
||||
export class UserActionState {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
|
||||
constructor(public userAction: UserAction) {}
|
||||
}
|
||||
@@ -374,7 +374,7 @@ export class VideoInformation {
|
||||
}
|
||||
|
||||
export class VolumeRequest {
|
||||
customData: any = null;
|
||||
customData: unknown = null;
|
||||
|
||||
constructor(public volume: Volume) {}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import cast, { ensureInit } from "../../export";
|
||||
import { Message } from "../../../messaging";
|
||||
import { ReceiverDevice } from "../../../types";
|
||||
|
||||
import type Session from "../../sdk/Session";
|
||||
import type Media from "../../sdk/media/Media";
|
||||
import type { Error as Error_ } from "../../sdk/classes";
|
||||
|
||||
function startMediaServer(
|
||||
filePath: string,
|
||||
port: number
|
||||
@@ -49,12 +53,12 @@ function startMediaServer(
|
||||
|
||||
let backgroundPort: MessagePort;
|
||||
|
||||
let currentSession: cast.Session;
|
||||
let currentMedia: cast.media.Media;
|
||||
let currentSession: Session;
|
||||
let currentMedia: Media;
|
||||
|
||||
let targetElement: HTMLElement;
|
||||
|
||||
function getSession(opts: InitOptions): Promise<cast.Session> {
|
||||
function getSession(opts: InitOptions): Promise<Session> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
/**
|
||||
* If a receiver is available, call requestSession. If a
|
||||
@@ -76,10 +80,10 @@ function getSession(opts: InitOptions): Promise<cast.Session> {
|
||||
// TODO: Handle this
|
||||
}
|
||||
|
||||
function onRequestSessionSuccess(session: cast.Session) {
|
||||
function onRequestSessionSuccess(session: Session) {
|
||||
resolve(session);
|
||||
}
|
||||
function onRequestSessionError(err: cast.Error) {
|
||||
function onRequestSessionError(err: Error_) {
|
||||
reject(err.description);
|
||||
}
|
||||
|
||||
@@ -97,7 +101,7 @@ function getSession(opts: InitOptions): Promise<cast.Session> {
|
||||
});
|
||||
}
|
||||
|
||||
function getMedia(opts: InitOptions): Promise<cast.media.Media> {
|
||||
function getMedia(opts: InitOptions): Promise<Media> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let mediaUrl = new URL(opts.mediaUrl);
|
||||
const mediaTitle = mediaUrl.pathname.slice(1);
|
||||
|
||||
@@ -6,6 +6,8 @@ import cast, { ensureInit } from "../export";
|
||||
import { ReceiverSelectorMediaType } from "../../background/receiverSelector";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
import type Session from "../sdk/Session";
|
||||
|
||||
// Variables passed from background
|
||||
const {
|
||||
selectedMedia,
|
||||
@@ -17,7 +19,7 @@ const {
|
||||
|
||||
const FX_CAST_RECEIVER_APP_NAMESPACE = "urn:x-cast:fx_cast";
|
||||
|
||||
let session: cast.Session;
|
||||
let session: Session;
|
||||
let wasSessionRequested = false;
|
||||
|
||||
let peerConnection: RTCPeerConnection;
|
||||
@@ -26,7 +28,7 @@ let peerConnection: RTCPeerConnection;
|
||||
* Sends a message to the fx_cast app running on the
|
||||
* receiver device.
|
||||
*/
|
||||
function sendAppMessage(subject: string, data: any) {
|
||||
function sendAppMessage(subject: string, data: unknown) {
|
||||
if (!session) {
|
||||
return;
|
||||
}
|
||||
@@ -41,7 +43,7 @@ window.addEventListener("beforeunload", () => {
|
||||
sendAppMessage("close", null);
|
||||
});
|
||||
|
||||
async function onRequestSessionSuccess(newSession: cast.Session) {
|
||||
async function onRequestSessionSuccess(newSession: Session) {
|
||||
cast.logMessage("onRequestSessionSuccess");
|
||||
|
||||
session = newSession;
|
||||
|
||||
4
ext/src/global.d.ts
vendored
4
ext/src/global.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
declare const BRIDGE_VERSION: string;
|
||||
declare const BRIDGE_NAME: string;
|
||||
declare const MIRRORING_APP_ID: string;
|
||||
@@ -68,7 +70,7 @@ type ExportFunctionFunc = (...args: any[]) => any;
|
||||
|
||||
declare function exportFunction(
|
||||
func: ExportFunctionFunc,
|
||||
targetScope: any,
|
||||
targetScope: Window,
|
||||
options?: ExportFunctionOptions
|
||||
): ExportFunctionFunc;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export class Logger {
|
||||
constructor(private prefix: string) {}
|
||||
|
||||
public log(message: string, data?: any) {
|
||||
public log(message: string, data?: unknown) {
|
||||
const formattedMessage = `${this.prefix} (Log): ${message}`;
|
||||
if (data) {
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -13,7 +13,7 @@ export class Logger {
|
||||
console.log(formattedMessage);
|
||||
}
|
||||
}
|
||||
public info(message: string, data?: any) {
|
||||
public info(message: string, data?: unknown) {
|
||||
const formattedMessage = `${this.prefix} (Info): ${message}`;
|
||||
if (data) {
|
||||
console.info(formattedMessage, data);
|
||||
@@ -21,7 +21,7 @@ export class Logger {
|
||||
console.info(formattedMessage);
|
||||
}
|
||||
}
|
||||
public warn(message: string, data?: any) {
|
||||
public warn(message: string, data?: unknown) {
|
||||
const formattedMessage = `${this.prefix} (Warning): ${message}`;
|
||||
if (data) {
|
||||
console.warn(formattedMessage, data);
|
||||
@@ -29,7 +29,7 @@ export class Logger {
|
||||
console.warn(formattedMessage);
|
||||
}
|
||||
}
|
||||
public error(message: string, data?: any) {
|
||||
public error(message: string, data?: unknown) {
|
||||
const formattedMessage = `${this.prefix} (Error): ${message}`;
|
||||
if (data) {
|
||||
console.error(formattedMessage, data);
|
||||
|
||||
@@ -32,7 +32,6 @@ function connectNative(application: string): Port {
|
||||
|
||||
// Port proxy API
|
||||
const portObject: Port = {
|
||||
error: null as any,
|
||||
name: "",
|
||||
|
||||
onDisconnect: {
|
||||
@@ -150,7 +149,7 @@ function connectNative(application: string): Port {
|
||||
}
|
||||
});
|
||||
|
||||
port.onMessage.addListener((message: any) => {
|
||||
port.onMessage.addListener((message: Message) => {
|
||||
if (!isNativeHostStatusKnown) {
|
||||
isNativeHostStatusKnown = true;
|
||||
messageQueue = [];
|
||||
@@ -177,8 +176,6 @@ async function sendNativeMessage(application: string, message: Message) {
|
||||
);
|
||||
}
|
||||
|
||||
const port = await options.get("bridgeBackupPort");
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
const ws = new WebSocket(
|
||||
`ws://${bridgeBackupHost}:${bridgeBackupPort}`
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
import logger from "./logger";
|
||||
|
||||
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
|
||||
|
||||
export function getNextEllipsis(ellipsis: string): string {
|
||||
@@ -18,7 +16,7 @@ export function getNextEllipsis(ellipsis: string): string {
|
||||
*/
|
||||
export function stringify(
|
||||
templateStrings: TemplateStringsArray,
|
||||
...substitutions: any[]
|
||||
...substitutions: unknown[]
|
||||
) {
|
||||
let formattedString = "";
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ type ExtMessageDefinitions = {
|
||||
defaultMediaType?: ReceiverSelectorMediaType;
|
||||
availableMediaTypes?: ReceiverSelectorMediaType;
|
||||
};
|
||||
"popup:close": {};
|
||||
"popup:close": undefined;
|
||||
|
||||
"receiverSelector:selected": ReceiverSelection;
|
||||
"receiverSelector:stop": ReceiverSelection;
|
||||
@@ -62,9 +62,9 @@ type ExtMessageDefinitions = {
|
||||
};
|
||||
"cast:selectReceiver/selected": ReceiverSelectionCast;
|
||||
"cast:selectReceiver/stopped": ReceiverSelectionStop;
|
||||
"cast:selectReceiver/cancelled": {};
|
||||
"cast:selectReceiver/cancelled": undefined;
|
||||
|
||||
"main:closeReceiverSelector": {};
|
||||
"main:closeReceiverSelector": undefined;
|
||||
|
||||
"main:initializeCast": { appId: string };
|
||||
"cast:initialized": BridgeInfo;
|
||||
@@ -221,12 +221,12 @@ type AppMessageDefinitions = {
|
||||
/**
|
||||
* Sent to bridge to stop HTTP media server.
|
||||
*/
|
||||
"bridge:stopMediaServer": {};
|
||||
"bridge:stopMediaServer": undefined;
|
||||
/**
|
||||
* Sent to media sender from bridge when the media server has
|
||||
* stopped.
|
||||
*/
|
||||
"mediaCast:mediaServerStopped": {};
|
||||
"mediaCast:mediaServerStopped": undefined;
|
||||
/**
|
||||
* Sent to media sender from bridge when the media server has
|
||||
* encountered an error.
|
||||
@@ -250,8 +250,8 @@ type Messages = {
|
||||
* all-optional keys.
|
||||
*/
|
||||
type NarrowedMessage<L extends MessageBase<keyof MessageDefinitions>> =
|
||||
L extends any
|
||||
? {} extends L["data"]
|
||||
L extends unknown
|
||||
? undefined extends L["data"]
|
||||
? Omit<L, "data"> & Partial<L>
|
||||
: L
|
||||
: never;
|
||||
@@ -264,22 +264,28 @@ export type Message = NarrowedMessage<Messages[keyof Messages]>;
|
||||
*/
|
||||
export default new (class Messenger {
|
||||
connect(connectInfo: { name: string }) {
|
||||
return browser.runtime.connect(connectInfo) as unknown as Port;
|
||||
return browser.runtime.connect(connectInfo) as Port;
|
||||
}
|
||||
|
||||
connectTab(tabId: number, connectInfo: { name: string; frameId: number }) {
|
||||
return browser.tabs.connect(tabId, connectInfo) as unknown as Port;
|
||||
return browser.tabs.connect(tabId, connectInfo) as Port;
|
||||
}
|
||||
|
||||
onConnect = {
|
||||
addListener(cb: (port: Port) => void) {
|
||||
browser.runtime.onConnect.addListener(cb as any);
|
||||
browser.runtime.onConnect.addListener(
|
||||
cb as (port: browser.runtime.Port) => void
|
||||
);
|
||||
},
|
||||
removeListener(cb: (port: Port) => void) {
|
||||
browser.runtime.onConnect.removeListener(cb as any);
|
||||
browser.runtime.onConnect.removeListener(
|
||||
cb as (port: browser.runtime.Port) => void
|
||||
);
|
||||
},
|
||||
hasListener(cb: (port: Port) => void) {
|
||||
return browser.runtime.onConnect.hasListener(cb as any);
|
||||
return browser.runtime.onConnect.hasListener(
|
||||
cb as (port: browser.runtime.Port) => void
|
||||
);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -4,14 +4,23 @@
|
||||
import React, { Component } from "react";
|
||||
import semver from "semver";
|
||||
|
||||
import options, { Options } from "../../lib/options";
|
||||
import { Options } from "../../lib/options";
|
||||
|
||||
import { BridgeInfo } from "../../lib/bridge";
|
||||
import { getNextEllipsis } from "../../lib/utils";
|
||||
|
||||
import logger from "../../lib/logger";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
interface Release {
|
||||
url: string;
|
||||
tag_name: string;
|
||||
html_url: string;
|
||||
assets: Array<{
|
||||
content_type: string;
|
||||
html_url: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface BridgeStatsProps {
|
||||
info: BridgeInfo;
|
||||
@@ -20,45 +29,42 @@ interface BridgeStatsProps {
|
||||
const BridgeStats = (props: BridgeStatsProps) => (
|
||||
<table className="bridge__stats">
|
||||
<tr>
|
||||
<th>{ _("optionsBridgeStatsName") }</th>
|
||||
<td>{ props.info.name }</td>
|
||||
<th>{_("optionsBridgeStatsName")}</th>
|
||||
<td>{props.info.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{ _("optionsBridgeStatsVersion") }</th>
|
||||
<td>{ props.info.version }</td>
|
||||
<th>{_("optionsBridgeStatsVersion")}</th>
|
||||
<td>{props.info.version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{ _("optionsBridgeStatsExpectedVersion") }</th>
|
||||
<td>{ props.info.expectedVersion }</td>
|
||||
<th>{_("optionsBridgeStatsExpectedVersion")}</th>
|
||||
<td>{props.info.expectedVersion}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{ _("optionsBridgeStatsCompatibility") }</th>
|
||||
<th>{_("optionsBridgeStatsCompatibility")}</th>
|
||||
<td>
|
||||
{ props.info.isVersionCompatible
|
||||
{props.info.isVersionCompatible
|
||||
? props.info.isVersionExact
|
||||
? _("optionsBridgeCompatible")
|
||||
: _("optionsBridgeLikelyCompatible")
|
||||
: _("optionsBridgeIncompatible") }
|
||||
: _("optionsBridgeIncompatible")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{ _("optionsBridgeStatsRecommendedAction") }</th>
|
||||
<th>{_("optionsBridgeStatsRecommendedAction")}</th>
|
||||
<td>
|
||||
{
|
||||
props.info.isVersionCompatible
|
||||
? _("optionsBridgeNoAction")
|
||||
: props.info.isVersionOlder
|
||||
? _("optionsBridgeOlderAction")
|
||||
: props.info.isVersionNewer
|
||||
? _("optionsBridgeNewerAction")
|
||||
: _("optionsBridgeNoAction")
|
||||
}
|
||||
{props.info.isVersionCompatible
|
||||
? _("optionsBridgeNoAction")
|
||||
: props.info.isVersionOlder
|
||||
? _("optionsBridgeOlderAction")
|
||||
: props.info.isVersionNewer
|
||||
? _("optionsBridgeNewerAction")
|
||||
: _("optionsBridgeNoAction")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
);
|
||||
|
||||
|
||||
interface BridgeProps {
|
||||
info?: BridgeInfo;
|
||||
loading: boolean;
|
||||
@@ -76,17 +82,17 @@ interface BridgeState {
|
||||
}
|
||||
|
||||
export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||
private updateData: any;
|
||||
private updateData?: Release;
|
||||
private updateStatusTimeout?: number;
|
||||
|
||||
constructor(props: BridgeProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isCheckingUpdates: false
|
||||
, isUpdateAvailable: false
|
||||
, wasErrorCheckingUpdates: false
|
||||
, checkUpdatesEllipsis: "..."
|
||||
isCheckingUpdates: false,
|
||||
isUpdateAvailable: false,
|
||||
wasErrorCheckingUpdates: false,
|
||||
checkUpdatesEllipsis: "..."
|
||||
};
|
||||
|
||||
this.onCheckUpdates = this.onCheckUpdates.bind(this);
|
||||
@@ -96,97 +102,123 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||
}
|
||||
|
||||
public render() {
|
||||
const [ backupMessageStart, backupMessageEnd ] =
|
||||
_("optionsBridgeBackupEnabled", "\0").split("\0");
|
||||
const [backupMessageStart, backupMessageEnd] = _(
|
||||
"optionsBridgeBackupEnabled",
|
||||
"\0"
|
||||
).split("\0");
|
||||
|
||||
return (
|
||||
<div className="bridge">
|
||||
{ this.props.loading
|
||||
? ( <div className="bridge__loading">
|
||||
{ _("optionsBridgeLoading") }
|
||||
<progress></progress>
|
||||
</div> )
|
||||
: this.renderStatus() }
|
||||
{this.props.loading ? (
|
||||
<div className="bridge__loading">
|
||||
{_("optionsBridgeLoading")}
|
||||
<progress></progress>
|
||||
</div>
|
||||
) : (
|
||||
this.renderStatus()
|
||||
)}
|
||||
|
||||
{ !this.props.loading && this.props.options &&
|
||||
{!this.props.loading && this.props.options && (
|
||||
<div className="bridge__options">
|
||||
<label className="option option--inline">
|
||||
<div className="option__control">
|
||||
<input name="bridgeBackupEnabled"
|
||||
type="checkbox"
|
||||
checked={ this.props.options.bridgeBackupEnabled }
|
||||
onChange={ this.props.onChange } />
|
||||
<input
|
||||
name="bridgeBackupEnabled"
|
||||
type="checkbox"
|
||||
checked={
|
||||
this.props.options.bridgeBackupEnabled
|
||||
}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="option__label">
|
||||
{ backupMessageStart }
|
||||
<input className="bridge__backup-host"
|
||||
name="bridgeBackupHost"
|
||||
type="text"
|
||||
required
|
||||
value={ this.props.options.bridgeBackupHost }
|
||||
onChange={ this.props.onChange } />
|
||||
{backupMessageStart}
|
||||
<input
|
||||
className="bridge__backup-host"
|
||||
name="bridgeBackupHost"
|
||||
type="text"
|
||||
required
|
||||
value={this.props.options.bridgeBackupHost}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
:
|
||||
<input className="bridge__backup-port"
|
||||
name="bridgeBackupPort"
|
||||
type="number"
|
||||
required
|
||||
min="1025"
|
||||
max="65535"
|
||||
value={ this.props.options.bridgeBackupPort }
|
||||
onChange={ this.props.onChange } />
|
||||
{ backupMessageEnd }
|
||||
<input
|
||||
className="bridge__backup-port"
|
||||
name="bridgeBackupPort"
|
||||
type="number"
|
||||
required
|
||||
min="1025"
|
||||
max="65535"
|
||||
value={this.props.options.bridgeBackupPort}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
{backupMessageEnd}
|
||||
</div>
|
||||
<div className="option__description">
|
||||
{ _("optionsBridgeBackupEnabledDescription") }
|
||||
{_("optionsBridgeBackupEnabledDescription")}
|
||||
</div>
|
||||
</label>
|
||||
</div> }
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ !this.props.loading &&
|
||||
{!this.props.loading && (
|
||||
<div className="bridge__update-info">
|
||||
{ this.state.isUpdateAvailable
|
||||
? ( <div className="bridge__update">
|
||||
<p className="bridge__update-label">
|
||||
{ _("optionsBridgeUpdateAvailable") }
|
||||
</p>
|
||||
<div className="bridge__update-options">
|
||||
<button className="bridge__update-start"
|
||||
type="button"
|
||||
onClick={ this.onUpdate }>
|
||||
{ _("optionsBridgeUpdate") }
|
||||
</button>
|
||||
</div>
|
||||
</div> )
|
||||
: ( <button className="bridge__update-check"
|
||||
{this.state.isUpdateAvailable ? (
|
||||
<div className="bridge__update">
|
||||
<p className="bridge__update-label">
|
||||
{_("optionsBridgeUpdateAvailable")}
|
||||
</p>
|
||||
<div className="bridge__update-options">
|
||||
<button
|
||||
className="bridge__update-start"
|
||||
type="button"
|
||||
disabled={ this.state.isCheckingUpdates }
|
||||
onClick={ this.onCheckUpdates }>
|
||||
|
||||
{ this.state.isCheckingUpdates
|
||||
? _("optionsBridgeUpdateChecking"
|
||||
, getNextEllipsis(this.state.checkUpdatesEllipsis))
|
||||
: _("optionsBridgeUpdateCheck") }
|
||||
</button> )}
|
||||
onClick={this.onUpdate}
|
||||
>
|
||||
{_("optionsBridgeUpdate")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="bridge__update-check"
|
||||
type="button"
|
||||
disabled={this.state.isCheckingUpdates}
|
||||
onClick={this.onCheckUpdates}
|
||||
>
|
||||
{this.state.isCheckingUpdates
|
||||
? _(
|
||||
"optionsBridgeUpdateChecking",
|
||||
getNextEllipsis(
|
||||
this.state.checkUpdatesEllipsis
|
||||
)
|
||||
)
|
||||
: _("optionsBridgeUpdateCheck")}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="bridge--update-status">
|
||||
{ this.state.updateStatus && !this.state.isUpdateAvailable
|
||||
&& this.state.updateStatus }
|
||||
{this.state.updateStatus &&
|
||||
!this.state.isUpdateAvailable &&
|
||||
this.state.updateStatus}
|
||||
</div>
|
||||
</div> }
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderStatus() {
|
||||
const infoClasses = `bridge__info ${!this.props.info
|
||||
? this.props.loadingTimedOut
|
||||
? "bridge__info--timed-out"
|
||||
: "bridge__info--not-found"
|
||||
: "bridge__info--found"}`;
|
||||
const infoClasses = `bridge__info ${
|
||||
!this.props.info
|
||||
? this.props.loadingTimedOut
|
||||
? "bridge__info--timed-out"
|
||||
: "bridge__info--not-found"
|
||||
: "bridge__info--found"
|
||||
}`;
|
||||
|
||||
let statusIcon: string;
|
||||
let statusTitle: string;
|
||||
let statusText: (string | null) = null;
|
||||
let statusText: string | null = null;
|
||||
|
||||
if (this.props.loadingTimedOut) {
|
||||
statusIcon = "assets/icons8-warn-120.png";
|
||||
@@ -208,22 +240,21 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||
return (
|
||||
<div className={infoClasses}>
|
||||
<div className="bridge__status">
|
||||
<img className="bridge__status-icon"
|
||||
width="60" height="60"
|
||||
src={ statusIcon } />
|
||||
<img
|
||||
className="bridge__status-icon"
|
||||
width="60"
|
||||
height="60"
|
||||
src={statusIcon}
|
||||
/>
|
||||
|
||||
<h2 className="bridge__status-title">
|
||||
{ statusTitle }
|
||||
</h2>
|
||||
<h2 className="bridge__status-title">{statusTitle}</h2>
|
||||
|
||||
{ statusText &&
|
||||
<p className="bridge__status-text">
|
||||
{ statusText }
|
||||
</p> }
|
||||
{statusText && (
|
||||
<p className="bridge__status-text">{statusText}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{ this.props.info &&
|
||||
<BridgeStats info={ this.props.info }/> }
|
||||
{this.props.info && <BridgeStats info={this.props.info} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -236,7 +267,8 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||
const timeout = window.setInterval(() => {
|
||||
this.setState(state => ({
|
||||
checkUpdatesEllipsis: getNextEllipsis(
|
||||
state.checkUpdatesEllipsis)
|
||||
state.checkUpdatesEllipsis
|
||||
)
|
||||
}));
|
||||
}, 500);
|
||||
|
||||
@@ -249,52 +281,58 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||
.catch(this.onCheckUpdatesError);
|
||||
}
|
||||
|
||||
private async onCheckUpdatesResponse(res: any) {
|
||||
private async onCheckUpdatesResponse(res: Release[]) {
|
||||
if (!Array.isArray(res)) {
|
||||
throw logger.error("Check update response is not array.", res);
|
||||
}
|
||||
|
||||
let latestBridgeRelease;
|
||||
for (const release of res) {
|
||||
if (release.assets.find((asset: any) =>
|
||||
asset.content_type !== "application/x-xpinstall")) {
|
||||
if (
|
||||
release.assets.find(
|
||||
asset => asset.content_type !== "application/x-xpinstall"
|
||||
)
|
||||
) {
|
||||
latestBridgeRelease = release;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!latestBridgeRelease) {
|
||||
this.setState({
|
||||
isCheckingUpdates: false
|
||||
, wasErrorCheckingUpdates: true
|
||||
, updateStatus: _("optionsBridgeUpdateStatusError")
|
||||
});
|
||||
|
||||
return;
|
||||
throw logger.error(
|
||||
"Check update response does not contain release info."
|
||||
);
|
||||
}
|
||||
|
||||
const isUpdateAvailable = !this.props.info || semver.lt(
|
||||
this.props.info.version, latestBridgeRelease.tag_name);
|
||||
/**
|
||||
* Update available if no bridge found or bridge version lower
|
||||
* than fetched release version.
|
||||
*/
|
||||
const isUpdateAvailable =
|
||||
!this.props.info ||
|
||||
semver.lt(this.props.info.version, latestBridgeRelease.tag_name);
|
||||
|
||||
if (isUpdateAvailable) {
|
||||
this.updateData = latestBridgeRelease;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isCheckingUpdates: false
|
||||
, isUpdateAvailable
|
||||
});
|
||||
|
||||
if (!isUpdateAvailable) {
|
||||
} else {
|
||||
this.setState({
|
||||
updateStatus: _("optionsBridgeUpdateStatusNoUpdates")
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isCheckingUpdates: false,
|
||||
isUpdateAvailable
|
||||
});
|
||||
|
||||
this.showUpdateStatus();
|
||||
}
|
||||
|
||||
private onCheckUpdatesError() {
|
||||
this.setState({
|
||||
isCheckingUpdates: false
|
||||
, wasErrorCheckingUpdates: true
|
||||
, updateStatus: _("optionsBridgeUpdateStatusError")
|
||||
isCheckingUpdates: false,
|
||||
wasErrorCheckingUpdates: true,
|
||||
updateStatus: _("optionsBridgeUpdateStatusError")
|
||||
});
|
||||
|
||||
this.showUpdateStatus();
|
||||
@@ -313,7 +351,7 @@ export default class Bridge extends Component<BridgeProps, BridgeState> {
|
||||
|
||||
private async onUpdate() {
|
||||
// Open downloads page
|
||||
if (this.updateData.html_url) {
|
||||
if (this.updateData?.html_url) {
|
||||
browser.tabs.create({
|
||||
url: this.updateData.html_url
|
||||
});
|
||||
|
||||
@@ -565,8 +565,6 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
||||
return;
|
||||
}
|
||||
|
||||
const application = status.applications?.[0];
|
||||
|
||||
if (this.state.showAlternateAction) {
|
||||
this.props.onStop(this.props.receiverDevice);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user