mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Add receiver selector options + misc options page improvements
This commit is contained in:
@@ -11,6 +11,43 @@ issues if you're going to work on something to avoid duplication of effort.
|
|||||||
Submit an issue for new features before submitting a PR.
|
Submit an issue for new features before submitting a PR.
|
||||||
|
|
||||||
|
|
||||||
## Compatibility Report
|
## Compatibility Reports
|
||||||
|
|
||||||
Compatibility reports are always helpful. Use the "Compatibility Report" issue template. Ensure you have a working environment and that the site is in the whitelist (check options page).
|
Compatibility reports are always helpful. Use the "Compatibility Report" issue template. Ensure you have a working environment and that the site is in the whitelist (check options page).
|
||||||
|
|
||||||
|
|
||||||
|
## Localizations
|
||||||
|
|
||||||
|
Missing strings:
|
||||||
|
|
||||||
|
* `es`
|
||||||
|
* `optionsMediaSyncElementDescription`
|
||||||
|
* `optionsReceiverSelectorCategoryName`
|
||||||
|
* `optionsReceiverSelectorCategoryDescription`
|
||||||
|
* `optionsReceiverSelectorType`
|
||||||
|
* `optionsReceiverSelectorTypeBrowser`
|
||||||
|
* `optionsReceiverSelectorTypeNative`
|
||||||
|
* `optionsReceiverSelectorWaitForConnection`
|
||||||
|
* `optionsReceiverSelectorWaitForConnectionDescription`
|
||||||
|
* `optionsReceiverSelectorCloseIfFocusLost`
|
||||||
|
* `optionsMirroringAppIdDescription`
|
||||||
|
* `nl`
|
||||||
|
* `popupMediaTypeApp`
|
||||||
|
* `popupMediaTypeTab`
|
||||||
|
* `popupMediaTypeScreen`
|
||||||
|
* `popupMediaTypeFile`
|
||||||
|
* `popupMediaSelectCastLabel`
|
||||||
|
* `popupMediaSelectToLabel`
|
||||||
|
* `contextAddToWhitelist`
|
||||||
|
* `contextAddToWhitelistRecommended`
|
||||||
|
* `contextAddToWhitelistAdvancedAdd`
|
||||||
|
* `optionsMediaSyncElementDescription`
|
||||||
|
* `optionsReceiverSelectorCategoryName`
|
||||||
|
* `optionsReceiverSelectorCategoryDescription`
|
||||||
|
* `optionsReceiverSelectorType`
|
||||||
|
* `optionsReceiverSelectorTypeBrowser`
|
||||||
|
* `optionsReceiverSelectorTypeNative`
|
||||||
|
* `optionsReceiverSelectorWaitForConnection`
|
||||||
|
* `optionsReceiverSelectorWaitForConnectionDescription`
|
||||||
|
* `optionsReceiverSelectorCloseIfFocusLost`
|
||||||
|
* `optionsMirroringAppIdDescription`
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true
|
"esModuleInterop": true
|
||||||
, "esModuleInterop": true
|
|
||||||
, "module": "commonjs"
|
, "module": "commonjs"
|
||||||
, "noImplicitAny": true
|
, "noImplicitAny": true
|
||||||
, "removeComments": true
|
, "removeComments": true
|
||||||
|
|||||||
@@ -193,6 +193,10 @@
|
|||||||
"message": "Sync receiver state with media element"
|
"message": "Sync receiver state with media element"
|
||||||
, "description": "Media casting sync checkbox label."
|
, "description": "Media casting sync checkbox label."
|
||||||
}
|
}
|
||||||
|
, "optionsMediaSyncElementDescription": {
|
||||||
|
"message": "Synchronize state (playback, volume, captions, etc...) between the media element and the receiver device."
|
||||||
|
, "description": "Media casting sync option description."
|
||||||
|
}
|
||||||
, "optionsMediaStopOnUnload": {
|
, "optionsMediaStopOnUnload": {
|
||||||
"message": "Stop receiver playback on page unload"
|
"message": "Stop receiver playback on page unload"
|
||||||
, "description": "Media stop on unload checkbox label."
|
, "description": "Media stop on unload checkbox label."
|
||||||
@@ -215,6 +219,39 @@
|
|||||||
, "description": "HTTP server port input label."
|
, "description": "HTTP server port input label."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
, "optionsReceiverSelectorCategoryName": {
|
||||||
|
"message": "Receiver selector"
|
||||||
|
, "description": "Options page receiver selector category title."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorCategoryDescription": {
|
||||||
|
"message": "Receiver device selection interface."
|
||||||
|
, "description": "Options page receiver selector category description."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorType": {
|
||||||
|
"message": "Type:"
|
||||||
|
, "description": "Receiver selector type option label."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorTypeBrowser": {
|
||||||
|
"message": "Browser"
|
||||||
|
, "description": "Receiver selector type browser radio option label."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorTypeNative": {
|
||||||
|
"message": "Native"
|
||||||
|
, "description": "Receiver selector type native radio option label."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorWaitForConnection": {
|
||||||
|
"message": "Wait for connection"
|
||||||
|
, "description": "Receiver selector wait for connection option checkbox label."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorWaitForConnectionDescription": {
|
||||||
|
"message": "Keep receiver selector open until the session is established or connection fails."
|
||||||
|
, "description": "Receiver selector wait for connection option description."
|
||||||
|
}
|
||||||
|
, "optionsReceiverSelectorCloseIfFocusLost": {
|
||||||
|
"message": "Close after losing focus"
|
||||||
|
, "description": "Receiver selector close if focus lost option checkbox label."
|
||||||
|
}
|
||||||
|
|
||||||
, "optionsUserAgentWhitelistCategoryName": {
|
, "optionsUserAgentWhitelistCategoryName": {
|
||||||
"message": "User agent whitelist"
|
"message": "User agent whitelist"
|
||||||
, "description": "Options page whitelist category title."
|
, "description": "Options page whitelist category title."
|
||||||
@@ -282,6 +319,10 @@
|
|||||||
"message": "Receiver app ID:"
|
"message": "Receiver app ID:"
|
||||||
, "description": "Mirroring app ID input label."
|
, "description": "Mirroring app ID input label."
|
||||||
}
|
}
|
||||||
|
, "optionsMirroringAppIdDescription": {
|
||||||
|
"message": "App ID for a registered Chromecast receiver application. Advanced use only. Must be compatible with the default app (see GitHub repo)."
|
||||||
|
, "description": "Mirroring app ID option description."
|
||||||
|
}
|
||||||
|
|
||||||
, "optionsReset": {
|
, "optionsReset": {
|
||||||
"message": "Restore Defaults"
|
"message": "Restore Defaults"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import { ReceiverSelectorType } from "./receiver_selectors";
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
bridgeApplicationName: string;
|
bridgeApplicationName: string;
|
||||||
mediaEnabled: boolean;
|
mediaEnabled: boolean;
|
||||||
@@ -9,6 +11,12 @@ export interface Options {
|
|||||||
localMediaServerPort: number;
|
localMediaServerPort: number;
|
||||||
mirroringEnabled: boolean;
|
mirroringEnabled: boolean;
|
||||||
mirroringAppId: string;
|
mirroringAppId: string;
|
||||||
|
receiverSelectorType: ReceiverSelectorType;
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
receiverSelectorCloseIfFocusLost: boolean;
|
||||||
|
receiverSelectorWaitForConnection: boolean;
|
||||||
|
|
||||||
userAgentWhitelistEnabled: boolean;
|
userAgentWhitelistEnabled: boolean;
|
||||||
userAgentWhitelist: string[];
|
userAgentWhitelist: string[];
|
||||||
|
|
||||||
@@ -24,6 +32,9 @@ const options: Options = {
|
|||||||
, localMediaServerPort: 9555
|
, localMediaServerPort: 9555
|
||||||
, mirroringEnabled: false
|
, mirroringEnabled: false
|
||||||
, mirroringAppId: MIRRORING_APP_ID
|
, mirroringAppId: MIRRORING_APP_ID
|
||||||
|
, receiverSelectorType: ReceiverSelectorType.Popup
|
||||||
|
, receiverSelectorCloseIfFocusLost: true
|
||||||
|
, receiverSelectorWaitForConnection: false
|
||||||
, userAgentWhitelistEnabled: true
|
, userAgentWhitelistEnabled: true
|
||||||
, userAgentWhitelist: [
|
, userAgentWhitelist: [
|
||||||
"https://www.netflix.com/*"
|
"https://www.netflix.com/*"
|
||||||
|
|||||||
@@ -639,11 +639,8 @@ async function onConnectShim (port: browser.runtime.Port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const { os } = await browser.runtime.getPlatformInfo();
|
const receiverSelector = getReceiverSelector(
|
||||||
|
await options.get("receiverSelectorType"));
|
||||||
const receiverSelector = getReceiverSelector(os === "mac"
|
|
||||||
? ReceiverSelectorType.NativeMac
|
|
||||||
: ReceiverSelectorType.Popup);
|
|
||||||
|
|
||||||
|
|
||||||
function onReceiverSelectorSelected (
|
function onReceiverSelectorSelected (
|
||||||
|
|||||||
@@ -47,27 +47,13 @@ export default class EditableList extends Component<
|
|||||||
public render () {
|
public render () {
|
||||||
return (
|
return (
|
||||||
<div className="editable-list">
|
<div className="editable-list">
|
||||||
<div className="editable-list__view-actions">
|
|
||||||
{ this.state.rawView &&
|
|
||||||
<button className="editable-list__save-raw-button"
|
|
||||||
onClick={ this.handleSaveRaw }
|
|
||||||
type="button">
|
|
||||||
{ _("optionsUserAgentWhitelistSaveRaw") }
|
|
||||||
</button> }
|
|
||||||
<button className="editable-list__view-button"
|
|
||||||
onClick={ this.handleSwitchView }
|
|
||||||
type="button">
|
|
||||||
{ this.state.rawView
|
|
||||||
? _("optionsUserAgentWhitelistBasicView")
|
|
||||||
: _("optionsUserAgentWhitelistRawView") }
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
{ this.state.rawView
|
{ this.state.rawView
|
||||||
? (
|
? (
|
||||||
<textarea className="editable-list__raw-view"
|
<textarea className="editable-list__raw-view"
|
||||||
rows={ this.props.data.length}
|
rows={ this.props.data.length > 10
|
||||||
value={ this.state.rawViewValue}
|
? this.props.data.length
|
||||||
|
: 10 }
|
||||||
|
value={ this.state.rawViewValue }
|
||||||
onChange={ this.handleRawViewTextAreaChange }
|
onChange={ this.handleRawViewTextAreaChange }
|
||||||
ref={ el => { this.rawViewTextArea = el; }}>
|
ref={ el => { this.rawViewTextArea = el; }}>
|
||||||
</textarea>
|
</textarea>
|
||||||
@@ -88,15 +74,30 @@ export default class EditableList extends Component<
|
|||||||
onEdit={ this.handleNewItemEdit }
|
onEdit={ this.handleNewItemEdit }
|
||||||
editing={ true } /> }
|
editing={ true } /> }
|
||||||
|
|
||||||
<div className="editable-list__item editable-list__item-actions">
|
|
||||||
<button className="editable-list__add-button"
|
|
||||||
onClick={ this.handleAddItem }
|
|
||||||
type="button">
|
|
||||||
{ _("optionsUserAgentWhitelistAddItem") }
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
<hr />
|
||||||
|
<div className="editable-list__view-actions">
|
||||||
|
{ !this.state.rawView &&
|
||||||
|
<button className="editable-list__add-button"
|
||||||
|
onClick={ this.handleAddItem }
|
||||||
|
type="button">
|
||||||
|
{ _("optionsUserAgentWhitelistAddItem") }
|
||||||
|
</button> }
|
||||||
|
{ this.state.rawView &&
|
||||||
|
<button className="editable-list__save-raw-button"
|
||||||
|
onClick={ this.handleSaveRaw }
|
||||||
|
type="button">
|
||||||
|
{ _("optionsUserAgentWhitelistSaveRaw") }
|
||||||
|
</button> }
|
||||||
|
<button className="editable-list__view-button"
|
||||||
|
onClick={ this.handleSwitchView }
|
||||||
|
type="button">
|
||||||
|
{ this.state.rawView
|
||||||
|
? _("optionsUserAgentWhitelistBasicView")
|
||||||
|
: _("optionsUserAgentWhitelistRawView") }
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import getBridgeInfo, { BridgeInfo } from "../../lib/getBridgeInfo";
|
|||||||
import options from "../../lib/options";
|
import options from "../../lib/options";
|
||||||
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils";
|
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils";
|
||||||
|
|
||||||
|
import { ReceiverSelectorType } from "../../receiver_selectors";
|
||||||
|
|
||||||
|
|
||||||
const _ = browser.i18n.getMessage;
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
@@ -73,6 +75,9 @@ 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.bind(this);
|
||||||
|
|
||||||
this.getWhitelistItemPatternError
|
this.getWhitelistItemPatternError
|
||||||
= this.getWhitelistItemPatternError.bind(this);
|
= this.getWhitelistItemPatternError.bind(this);
|
||||||
}
|
}
|
||||||
@@ -117,58 +122,66 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option option--inline">
|
<label className="option option--inline">
|
||||||
<input name="mediaEnabled"
|
<div className="option__control">
|
||||||
type="checkbox"
|
<input name="mediaEnabled"
|
||||||
checked={ this.state.options.mediaEnabled }
|
type="checkbox"
|
||||||
onChange={ this.handleInputChange } />
|
checked={ this.state.options.mediaEnabled }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsMediaEnabled") }
|
{ _("optionsMediaEnabled") }
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="option option--inline">
|
<label className="option option--inline">
|
||||||
<input name="mediaSyncElement"
|
<div className="option__control">
|
||||||
type="checkbox"
|
<input name="mediaSyncElement"
|
||||||
checked={ this.state.options.mediaSyncElement }
|
type="checkbox"
|
||||||
onChange={ this.handleInputChange } />
|
checked={ this.state.options.mediaSyncElement }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsMediaSyncElement") }
|
{ _("optionsMediaSyncElement") }
|
||||||
</div>
|
</div>
|
||||||
|
<div className="option__description">
|
||||||
|
{ _("optionsMediaSyncElementDescription") }
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="option option--inline">
|
<label className="option option--inline">
|
||||||
<input name="mediaStopOnUnload"
|
<div className="option__control">
|
||||||
type="checkbox"
|
<input name="mediaStopOnUnload"
|
||||||
checked={ this.state.options.mediaStopOnUnload }
|
type="checkbox"
|
||||||
onChange={ this.handleInputChange } />
|
checked={ this.state.options.mediaStopOnUnload }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsMediaStopOnUnload") }
|
{ _("optionsMediaStopOnUnload") }
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<fieldset className="category"
|
<hr />
|
||||||
disabled={ !this.state.options.mediaEnabled }>
|
|
||||||
<legend className="category__name">
|
|
||||||
<h2>{ _("optionsLocalMediaCategoryName") }</h2>
|
|
||||||
</legend>
|
|
||||||
<p className="category__description">
|
|
||||||
{ _("optionsLocalMediaCategoryDescription") }
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<label className="option option--inline">
|
<label className="option option--inline">
|
||||||
|
<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 className="option__label">
|
</div>
|
||||||
{ _("optionsLocalMediaEnabled") }
|
<div className="option__label">
|
||||||
</div>
|
{ _("optionsLocalMediaEnabled") }
|
||||||
</label>
|
</div>
|
||||||
|
<div className="option__description">
|
||||||
|
{ _("optionsLocalMediaCategoryDescription") }
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsLocalMediaServerPort") }
|
{ _("optionsLocalMediaServerPort") }
|
||||||
</div>
|
</div>
|
||||||
|
<div className="option__control">
|
||||||
<input name="localMediaServerPort"
|
<input name="localMediaServerPort"
|
||||||
type="number"
|
type="number"
|
||||||
required
|
required
|
||||||
@@ -176,8 +189,8 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
max="65535"
|
max="65535"
|
||||||
value={ this.state.options.localMediaServerPort }
|
value={ this.state.options.localMediaServerPort }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</div>
|
||||||
</fieldset>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
@@ -189,10 +202,12 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option option--inline">
|
<label className="option option--inline">
|
||||||
<input name="mirroringEnabled"
|
<div className="option__control">
|
||||||
type="checkbox"
|
<input name="mirroringEnabled"
|
||||||
checked={ this.state.options.mirroringEnabled }
|
type="checkbox"
|
||||||
onChange={ this.handleInputChange } />
|
checked={ this.state.options.mirroringEnabled }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsMirroringEnabled") }
|
{ _("optionsMirroringEnabled") }
|
||||||
</div>
|
</div>
|
||||||
@@ -202,11 +217,71 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsMirroringAppId") }
|
{ _("optionsMirroringAppId") }
|
||||||
</div>
|
</div>
|
||||||
<input name="mirroringAppId"
|
<div className="option__control">
|
||||||
type="text"
|
<input name="mirroringAppId"
|
||||||
required
|
type="text"
|
||||||
value={ this.state.options.mirroringAppId }
|
required
|
||||||
onChange={ this.handleInputChange } />
|
value={ this.state.options.mirroringAppId }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
<div className="option__description">
|
||||||
|
{ _("optionsMirroringAppIdDescription") }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset className="category">
|
||||||
|
<legend className="category__name">
|
||||||
|
<h2>{ _("optionsReceiverSelectorCategoryName") }</h2>
|
||||||
|
</legend>
|
||||||
|
<p className="category__description">
|
||||||
|
{ _("optionsReceiverSelectorCategoryDescription") }
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{ this.state.platform === "mac" &&
|
||||||
|
<label className="option">
|
||||||
|
<div className="option__label">
|
||||||
|
{ _("optionsReceiverSelectorType") }
|
||||||
|
</div>
|
||||||
|
<div className="option__control">
|
||||||
|
<select name="receiverSelectorType"
|
||||||
|
value={ this.state.options.receiverSelectorType }
|
||||||
|
onChange={ this.handleReceiverSelectorTypeChange }>
|
||||||
|
<option value={ ReceiverSelectorType.Popup }>
|
||||||
|
{ _("optionsReceiverSelectorTypeBrowser") }
|
||||||
|
</option>
|
||||||
|
<option value={ ReceiverSelectorType.NativeMac }>
|
||||||
|
{ _("optionsReceiverSelectorTypeNative") }
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</label> }
|
||||||
|
|
||||||
|
<label className="option option--inline">
|
||||||
|
<div className="option__control">
|
||||||
|
<input name="receiverSelectorWaitForConnection"
|
||||||
|
type="checkbox"
|
||||||
|
checked={ this.state.options.receiverSelectorWaitForConnection }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
|
<div className="option__label">
|
||||||
|
{ _("optionsReceiverSelectorWaitForConnection") }
|
||||||
|
</div>
|
||||||
|
<div className="option__description">
|
||||||
|
{ _("optionsReceiverSelectorWaitForConnectionDescription") }
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="option option--inline">
|
||||||
|
<div className="option__control">
|
||||||
|
<input name="receiverSelectorCloseIfFocusLost"
|
||||||
|
type="checkbox"
|
||||||
|
checked={ this.state.options.receiverSelectorCloseIfFocusLost }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
|
<div className="option__label">
|
||||||
|
{ _("optionsReceiverSelectorCloseIfFocusLost") }
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -219,10 +294,12 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option option--inline">
|
<label className="option option--inline">
|
||||||
<input name="userAgentWhitelistEnabled"
|
<div className="option__control">
|
||||||
type="checkbox"
|
<input name="userAgentWhitelistEnabled"
|
||||||
checked={ this.state.options.userAgentWhitelistEnabled }
|
type="checkbox"
|
||||||
onChange={ this.handleInputChange } />
|
checked={ this.state.options.userAgentWhitelistEnabled }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</div>
|
||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsUserAgentWhitelistEnabled") }
|
{ _("optionsUserAgentWhitelistEnabled") }
|
||||||
</div>
|
</div>
|
||||||
@@ -232,10 +309,12 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
<div className="option__label">
|
<div className="option__label">
|
||||||
{ _("optionsUserAgentWhitelistContent") }
|
{ _("optionsUserAgentWhitelistContent") }
|
||||||
</div>
|
</div>
|
||||||
<EditableList data={ this.state.options.userAgentWhitelist }
|
<div className="option__control">
|
||||||
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>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@@ -311,10 +390,17 @@ class OptionsApp extends Component<{}, OptionsAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleInputChange (ev: React.ChangeEvent<HTMLInputElement>) {
|
private handleInputChange (ev: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const { target } = ev;
|
this.setState(currentState => {
|
||||||
|
currentState.options[ev.target.name] = getInputValue(ev.target);
|
||||||
|
return currentState;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleReceiverSelectorTypeChange (
|
||||||
|
ev: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
|
||||||
this.setState(currentState => {
|
this.setState(currentState => {
|
||||||
currentState.options[target.name] = getInputValue(target);
|
currentState.options[ev.target.name] = parseInt(ev.target.value);
|
||||||
return currentState;
|
return currentState;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
box-shadow: 0 0 1.5px 1px red;
|
box-shadow: 0 0 1.5px 1px red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
#form {
|
#form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -27,7 +31,6 @@
|
|||||||
color: var(--secondary-color);
|
color: var(--secondary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.bridge {
|
.bridge {
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@@ -172,14 +175,12 @@
|
|||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.category > .category {
|
|
||||||
padding: 5px 0;
|
|
||||||
box-shadow: inset 2px 0 0 0 var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.category > .category > .category__name,
|
.category > hr {
|
||||||
.category > .category > .category__description {
|
border: initial;
|
||||||
margin-inline-start: 16px;
|
border-top: 1px solid var(--border-color);
|
||||||
|
grid-column: span 2;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category__name {
|
.category__name {
|
||||||
@@ -210,8 +211,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.option--inline {
|
.option--inline {
|
||||||
display: block;
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
grid-column-start: 2;
|
grid-column-start: 2;
|
||||||
|
grid-template-columns: min-content 1fr;
|
||||||
|
grid-template-rows: min-content min-content;
|
||||||
|
grid-template-areas:
|
||||||
|
"input label"
|
||||||
|
". description";
|
||||||
|
}
|
||||||
|
|
||||||
|
.option--inline > input {
|
||||||
|
grid-area: input;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
.option--inline > .option__label {
|
||||||
|
grid-area: label;
|
||||||
|
text-align: initial;
|
||||||
|
}
|
||||||
|
.option--inline > .option__description {
|
||||||
|
grid-area: description;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option__label {
|
.option__label {
|
||||||
@@ -219,7 +238,16 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option > input {
|
.option__description {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
font-size: smaller;
|
||||||
|
grid-column: span 2;
|
||||||
|
margin: 5px 0;
|
||||||
|
max-width: 45ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option > input,
|
||||||
|
.option > select {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
justify-self: flex-start;
|
justify-self: flex-start;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
@@ -239,9 +267,8 @@
|
|||||||
justify-content: end;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-list__view-button,
|
|
||||||
.editable-list__save-raw-button {
|
.editable-list__save-raw-button {
|
||||||
|
margin-inline-end: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-list hr {
|
.editable-list hr {
|
||||||
@@ -266,10 +293,10 @@
|
|||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editable-list__item:nth-child(even):not(:last-child) {
|
.editable-list__item:nth-child(even) {
|
||||||
background-color: -moz-eventreerow;
|
background-color: -moz-eventreerow;
|
||||||
}
|
}
|
||||||
.editable-list__item:nth-child(odd):not(:last-child) {
|
.editable-list__item:nth-child(odd) {
|
||||||
background-color: -moz-oddtreerow;
|
background-color: -moz-oddtreerow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +309,10 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editable-list__title + button {
|
||||||
|
margin-inline-end: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.editable-list__edit-field {
|
.editable-list__edit-field {
|
||||||
width: -moz-available;
|
width: -moz-available;
|
||||||
margin-inline-end: 1em;
|
margin-inline-end: 1em;
|
||||||
@@ -294,6 +325,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editable-list__add-button {
|
.editable-list__add-button {
|
||||||
align-self: end;
|
margin-inline-end: auto;
|
||||||
margin-top: 5px;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,11 @@ button,
|
|||||||
select {
|
select {
|
||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
height: 16px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
margin-top: 1px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user