mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 01:29:58 +00:00
Implement receiver selector whitelist suggestion banner
This commit is contained in:
@@ -12,7 +12,7 @@ import EditableList from "./EditableList";
|
||||
import bridge, { BridgeInfo, BridgeTimedOutError } from "../../lib/bridge";
|
||||
import logger from "../../lib/logger";
|
||||
import options, { Options } from "../../lib/options";
|
||||
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils";
|
||||
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/matchPattern";
|
||||
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
--shadow-10: 0 1px 4px rgba(12, 12, 13, 0.1);
|
||||
--shadow-20: 0 2px 8px rgba(12, 12, 13, 0.1);
|
||||
--shadow-30: 0 4px 16px rgba(12, 12, 13, 0.1);
|
||||
|
||||
|
||||
--focus-border-color: var(--blue-50);
|
||||
|
||||
--box-background: var(--white-100);
|
||||
--box-color: var(--grey-90);
|
||||
|
||||
--focus-box-shadow:
|
||||
0 0 0 1px var(--focus-border-color);
|
||||
--focus-box-shadow: 0 0 0 1px var(--focus-border-color);
|
||||
|
||||
--button-background: var(--grey-90-a10);
|
||||
--button-background-hover: var(--grey-90-a20);
|
||||
@@ -26,12 +25,10 @@
|
||||
--field-border-color: var(--grey-90-a20);
|
||||
--field-border-color-hover: var(--grey-90-a30);
|
||||
|
||||
--field-box-shadow-warning:
|
||||
0 0 0 1px var(--yellow-60)
|
||||
, 0 0 0 4px var(--yellow-60-a30);
|
||||
--field-box-shadow-error:
|
||||
0 0 0 1px var(--red-60)
|
||||
, 0 0 0 4px var(--red-60-a30);
|
||||
--field-box-shadow-warning: 0 0 0 1px var(--yellow-60),
|
||||
0 0 0 4px var(--yellow-60-a30);
|
||||
--field-box-shadow-error: 0 0 0 1px var(--red-60),
|
||||
0 0 0 4px var(--red-60-a30);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -102,7 +99,9 @@ textarea:invalid {
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
input:disabled {
|
||||
input:disabled,
|
||||
textarea:disabled,
|
||||
select:disabled {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
@@ -111,6 +110,7 @@ input,
|
||||
textarea,
|
||||
select {
|
||||
padding: 4px 8px;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* No inset for spinbox control */
|
||||
@@ -130,13 +130,14 @@ button:default:hover:active {
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
--arrow-width: 20px;
|
||||
--arrow-width: 16px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.select-wrapper::after {
|
||||
align-items: center;
|
||||
content: "▼";
|
||||
opacity: 0.5;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
margin-right: 4px;
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import knownApps from "../../cast/knownApps";
|
||||
import knownApps, { KnownApp } from "../../cast/knownApps";
|
||||
import options from "../../lib/options";
|
||||
|
||||
import messaging, { Message, Port } from "../../messaging";
|
||||
import { getNextEllipsis } from "../../lib/utils";
|
||||
import { RemoteMatchPattern } from "../../lib/matchPattern";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
import {
|
||||
ReceiverSelectionActionType,
|
||||
ReceiverSelectorMediaType
|
||||
} from "../../background/receiverSelector";
|
||||
import { PageInfo } from "../../background/receiverSelector/ReceiverSelector";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
@@ -37,8 +39,14 @@ interface PopupAppState {
|
||||
|
||||
filePath?: string;
|
||||
appId?: string;
|
||||
pageInfo?: PageInfo;
|
||||
|
||||
mirroringEnabled: boolean;
|
||||
userAgentWhitelistEnabled: boolean;
|
||||
userAgentWhitelist: string[];
|
||||
|
||||
knownApp?: KnownApp;
|
||||
isPageWhitelisted: boolean;
|
||||
}
|
||||
|
||||
class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
@@ -54,7 +62,10 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
mediaType: ReceiverSelectorMediaType.App,
|
||||
availableMediaTypes: ReceiverSelectorMediaType.App,
|
||||
isLoading: false,
|
||||
mirroringEnabled: false
|
||||
mirroringEnabled: false,
|
||||
userAgentWhitelistEnabled: true,
|
||||
userAgentWhitelist: [],
|
||||
isPageWhitelisted: false
|
||||
};
|
||||
|
||||
// Store window ref
|
||||
@@ -66,6 +77,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
this.updateWindowHeight();
|
||||
}).observe(document.body);
|
||||
|
||||
this.onAddToWhitelist = this.onAddToWhitelist.bind(this);
|
||||
this.onSelectChange = this.onSelectChange.bind(this);
|
||||
this.onCast = this.onCast.bind(this);
|
||||
this.onStop = this.onStop.bind(this);
|
||||
@@ -91,7 +103,8 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
switch (message.subject) {
|
||||
case "popup:init": {
|
||||
this.setState({
|
||||
appId: message.data?.appId
|
||||
appId: message.data?.appId,
|
||||
pageInfo: message.data?.pageInfo
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -114,6 +127,8 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
});
|
||||
}
|
||||
|
||||
this.updateKnownApp();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -124,9 +139,15 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
}
|
||||
});
|
||||
|
||||
const opts = await options.getAll();
|
||||
|
||||
this.setState({
|
||||
mirroringEnabled: await options.get("mirroringEnabled")
|
||||
mirroringEnabled: opts.mirroringEnabled,
|
||||
userAgentWhitelistEnabled: opts.userAgentWhitelistEnabled,
|
||||
userAgentWhitelist: opts.userAgentWhitelist
|
||||
});
|
||||
|
||||
this.updateKnownApp();
|
||||
}
|
||||
|
||||
public componentDidUpdate() {
|
||||
@@ -135,6 +156,58 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
}, 1);
|
||||
}
|
||||
|
||||
private updateKnownApp() {
|
||||
const isAppMediaTypeAvailable = !!(
|
||||
this.state.availableMediaTypes & ReceiverSelectorMediaType.App
|
||||
);
|
||||
|
||||
let knownApp: Nullable<KnownApp> = null;
|
||||
|
||||
/**
|
||||
* Check knownApps for an app with an ID matching the registered
|
||||
* app on the target page.
|
||||
* Or if there isn't an registered app, check for an app with a
|
||||
* match pattern matching the target page URL.
|
||||
*/
|
||||
if (isAppMediaTypeAvailable && this.state.appId) {
|
||||
knownApp = knownApps[this.state.appId];
|
||||
} else if (this.state.pageInfo) {
|
||||
const pageUrl = this.state.pageInfo.url;
|
||||
|
||||
for (const [, app] of Object.entries(knownApps)) {
|
||||
if (!app.matches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pattern = new RemoteMatchPattern(app.matches);
|
||||
if (pattern.matches(pageUrl)) {
|
||||
knownApp = app;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let isPageWhitelisted = false;
|
||||
|
||||
/**
|
||||
* Check if target page URL is whitelisted.
|
||||
*/
|
||||
if (this.state.pageInfo) {
|
||||
for (const patternString of this.state.userAgentWhitelist) {
|
||||
const pattern = new RemoteMatchPattern(patternString);
|
||||
if (pattern.matches(this.state.pageInfo.url)) {
|
||||
isPageWhitelisted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
knownApp: knownApp ?? undefined,
|
||||
isPageWhitelisted
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
/*
|
||||
|
||||
@@ -166,9 +239,42 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
this.state.availableMediaTypes & ReceiverSelectorMediaType.App
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="whitelist-suggest"
|
||||
hidden={
|
||||
// If we don't know the app
|
||||
!this.state.knownApp ||
|
||||
// If the whitelist is disabled
|
||||
!this.state.userAgentWhitelistEnabled ||
|
||||
// If the whitelist is enabled, and the page is whitelisted
|
||||
(this.state.userAgentWhitelistEnabled &&
|
||||
this.state.isPageWhitelisted) ||
|
||||
// If an app is already loaded on the page
|
||||
isAppMediaTypeAvailable
|
||||
}
|
||||
>
|
||||
<img src="photon_info.svg" />
|
||||
{_(
|
||||
"popupWhitelistNotWhitelisted",
|
||||
this.state.knownApp?.name
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!this.state.knownApp || !this.state.pageInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onAddToWhitelist(
|
||||
this.state.knownApp,
|
||||
this.state.pageInfo
|
||||
);
|
||||
}}
|
||||
>
|
||||
{_("popupWhitelistAddToWhitelist")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="media-select">
|
||||
<div className="media-select__label-cast">
|
||||
{_("popupMediaSelectCastLabel")}
|
||||
@@ -187,8 +293,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
selected={isAppMediaTypeSelected}
|
||||
disabled={!isAppMediaTypeAvailable}
|
||||
>
|
||||
{(this.state.appId &&
|
||||
knownApps[this.state.appId]?.name) ??
|
||||
{this.state.knownApp?.name ??
|
||||
_("popupMediaTypeApp")}
|
||||
</option>
|
||||
|
||||
@@ -248,6 +353,21 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
);
|
||||
}
|
||||
|
||||
private async onAddToWhitelist(app: KnownApp, pageInfo: PageInfo) {
|
||||
if (!app.matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
const whitelist = await options.get("userAgentWhitelist");
|
||||
if (!whitelist.includes(app.matches)) {
|
||||
whitelist.push(app.matches);
|
||||
await options.set("userAgentWhitelist", whitelist);
|
||||
|
||||
await browser.tabs.reload(pageInfo.tabId);
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
private onCast(receiver: ReceiverDevice) {
|
||||
this.setState({
|
||||
isLoading: true
|
||||
|
||||
13
ext/src/ui/popup/photon_info.svg
Normal file
13
ext/src/ui/popup/photon_info.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path {
|
||||
fill: rgba(249, 249, 250, .8);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<path fill="rgba(12, 12, 13, .8)" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 710 B |
@@ -1,9 +1,3 @@
|
||||
:root {
|
||||
--button-background: #474749;
|
||||
--button-background-hover: #505054;
|
||||
--button-background-active: #5c5c5e;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--grey-10);
|
||||
color: var(--grey-90);
|
||||
@@ -12,6 +6,10 @@ body {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: var(--grey-80) !important;
|
||||
@@ -26,8 +24,35 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.media-select {
|
||||
.whitelist-suggest {
|
||||
align-items: center;
|
||||
background-color: var(--blue-50-a30);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
font-size: 0.9em;
|
||||
gap: 0.5em;
|
||||
padding: 0.75em;
|
||||
}
|
||||
.whitelist-suggest > button {
|
||||
--button-background: hsla(0, 0%, 50%, 0.3);
|
||||
--button-background-hover: hsla(0, 0%, 30%, 0.3);
|
||||
--button-background-active: hsla(0, 0%, 10%, 0.3);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.whitelist-suggest {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.whitelist-suggest > button {
|
||||
--button-background: hsla(0, 0%, 50%, 0.3);
|
||||
--button-background-hover: hsla(0, 0%, 70%, 0.3);
|
||||
--button-background-active: hsla(0, 0%, 90%, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.media-select {
|
||||
align-items: baseline;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
margin: 0 1em;
|
||||
@@ -45,11 +70,6 @@ body {
|
||||
margin-inline-start: 0.5em;
|
||||
}
|
||||
|
||||
.media-select__dropdown {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.receivers {
|
||||
list-style: none;
|
||||
margin: initial;
|
||||
|
||||
Reference in New Issue
Block a user