Change message format + refactor options

This commit is contained in:
hensm
2018-11-28 08:35:31 +00:00
parent 5ee6b25cb7
commit 70246f1f99
11 changed files with 423 additions and 401 deletions

View File

@@ -1,97 +1,97 @@
{
"extension_name": {
"extensionName": {
"message": "EXTENSION_NAME"
}
, "extension_description": {
, "extensionDescription": {
"message": ""
}
, "popup_cast_button_label": {
, "popupCastButtonLabel": {
"message": "Cast"
}
, "popup_casting_button_label": {
, "popupCastingButtonLabel": {
"message": "Casting"
}
, "context_media_cast": {
, "contextCast": {
"message": "Cast..."
}
, "options_category_media": {
, "optionsMediaCategoryName": {
"message": "Media casting"
}
, "options_category_media_description": {
, "optionsMediaCategoryDescription": {
"message": "HTML5 video/audio media casting."
}
, "options_option_mediaEnabled": {
, "optionsMediaEnabled": {
"message": "Enabled"
}
, "options_category_localMedia": {
, "optionsLocalMediaCategoryName": {
"message": "Local media casting"
}
, "options_category_localMedia_description": {
, "optionsLocalMediaCategoryDescription": {
"message": "HTTP server started by the bridge app to stream local media files to the cast receiver."
}
, "options_option_localMediaEnabled": {
, "optionsLocalMediaEnabled": {
"message": "Enabled"
}
, "options_option_localMediaServerPort": {
, "optionsLocalMediaServerPort": {
"message": "HTTP server port"
}
, "options_category_uaWhitelist": {
, "optionsUserAgentWhitelistCategoryName": {
"message": "User agent whitelist"
}
, "options_category_uaWhitelist_description": {
, "optionsUserAgentWhitelistCategoryDescription": {
"message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns."
}
, "options_option_uaWhitelistEnabled": {
, "optionsUserAgentWhitelistEnabled": {
"message": "Enabled"
}
, "options_option_uaWhitelist": {
, "optionsUserAgentWhitelistContent": {
"message": "Match patterns (newline-separated)"
}
, "options_option_uaWhitelistBasicView": {
, "optionsUserAgentWhitelistBasicView": {
"message": "Basic View"
}
, "options_option_uaWhitelistRawView": {
, "optionsUserAgentWhitelistRawView": {
"message": "Raw View"
}
, "options_option_uaWhitelistSaveRaw": {
, "optionsUserAgentWhitelistSaveRaw": {
"message": "Save Raw"
}
, "options_option_uaWhitelistAddItem": {
, "optionsUserAgentWhitelistAddItem": {
"message": "Add Item"
}
, "options_option_uaWhitelistItemEdit": {
, "optionsUserAgentWhitelistEditItem": {
"message": "Edit"
}
, "options_option_uaWhitelistItemRemove": {
, "optionsUserAgentWhitelistRemoveItem": {
"message": "Remove"
}
, "options_option_uaWhitelistInvalidMatchPattern": {
, "optionsUserAgentWhitelistInvalidMatchPattern": {
"message": "Invalid match pattern $1"
}
, "options_category_mirroring": {
, "optionsMirroringCategoryName": {
"message": "Screen mirroring"
}
, "options_category_mirroring_description": {
, "optionsMirroringCategoryDescription": {
"message": "Screen/Tab mirroring to a Chromecast receiver app."
}
, "options_option_mirroringEnabled": {
, "optionsMirroringEnabled": {
"message": "Enabled"
}
, "options_option_mirroringAppId": {
, "optionsMirroringAppId": {
"message": "Receiver app ID"
}
, "options_reset": {
, "optionsReset": {
"message": "Reset to defaults"
}
, "options_submit": {
, "optionsSubmit": {
"message": "Submit"
}
}

View File

@@ -10,20 +10,23 @@ browser.runtime.onInstalled.addListener(async details => {
switch (details.reason) {
// Set default options
case "install":
case "install": {
await browser.storage.sync.set({
options: defaultOptions
});
break;
};
// Set newly added options
case "update":
const { options } = await browser.storage.sync.get("options");
case "update": {
const { options: existingOptions }
= await browser.storage.sync.get("options");
const newOptions = {};
// Find options not already in storage
for (const [ key, val ] of Object.entries(defaultOptions)) {
if (!options.hasOwnProperty(key)) {
if (!existingOptions.hasOwnProperty(key)) {
newOptions[key] = val;
}
}
@@ -31,12 +34,13 @@ browser.runtime.onInstalled.addListener(async details => {
// Update storage with default values of new options
await browser.storage.sync.set({
options: {
...options
...existingOptions
, ...newOptions
}
});
break;
};
}
// Call after default options have been set
@@ -64,7 +68,7 @@ async function createMenus () {
*/
if (!options || mirrorCastMenuId || mediaCastMenuId) return;
if (options.option_localMediaEnabled) {
if (options.localMediaEnabled) {
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
}
@@ -73,16 +77,16 @@ async function createMenus () {
contexts: [ "audio", "video" ]
, id: "contextCastMedia"
, targetUrlPatterns: Array.from(mediaCastTargetUrlPatterns)
, title: _("context_media_cast")
, visible: options.option_mediaEnabled
, title: _("contextCast")
, visible: options.mediaEnabled
});
// Screen/Tab mirroring "Cast..." context menu item
mirrorCastMenuId = await browser.menus.create({
contexts: [ "browser_action", "page" ]
, id: "contextCast"
, title: _("context_media_cast")
, visible: options.option_mirroringEnabled
, title: _("contextCast")
, visible: options.mirroringEnabled
});
}
@@ -190,8 +194,8 @@ async function onOptionsUpdated (alteredOptions) {
onBeforeSendHeaders () {
browser.webRequest.onBeforeSendHeaders.addListener(
onBeforeSendHeaders
, { urls: options.option_uaWhitelistEnabled
? options.option_uaWhitelist
, { urls: options.userAgentWhitelistEnabled
? options.userAgentWhitelist
: [] }
, [ "blocking", "requestHeaders" ]);
}
@@ -203,26 +207,26 @@ async function onOptionsUpdated (alteredOptions) {
func();
}
} else {
if (alteredOptions.includes("option_uaWhitelist")
|| alteredOptions.includes("option_uaWhitelistEnabled")) {
if (alteredOptions.includes("userAgentWhitelist")
|| alteredOptions.includes("userAgentWhitelistEnabled")) {
browser.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
registerFunctions.onBeforeSendHeaders();
}
if (alteredOptions.includes("option_mirroringEnabled")) {
if (alteredOptions.includes("mirroringEnabled")) {
browser.menus.update(mirrorCastMenuId, {
visible: options.option_mirroringEnabled
visible: options.mirroringEnabled
});
}
if (alteredOptions.includes("option_mediaEnabled")) {
if (alteredOptions.includes("mediaEnabled")) {
browser.menus.update(mediaCastMenuId, {
visible: options.option_mediaEnabled
visible: options.mediaEnabled
})
}
if (alteredOptions.includes("option_localMediaEnabled")) {
if (options.option_localMediaEnabled) {
if (alteredOptions.includes("localMediaEnabled")) {
if (options.localMediaEnabled) {
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
} else {
mediaCastTargetUrlPatterns.delete(LOCAL_MEDIA_URL_PATTERN);
@@ -276,7 +280,7 @@ browser.menus.onClicked.addListener(async (info, tab) => {
await browser.tabs.executeScript(tab.id, {
code: `let selectedMedia = "${info.pageUrl ? "tab" : "screen"}";
let FX_CAST_RECEIVER_APP_ID = "${options.option_mirroringAppId}";`
let FX_CAST_RECEIVER_APP_ID = "${options.mirroringEnabled}";`
, frameId
});

View File

@@ -1,6 +1,6 @@
{
"name": "__MSG_extension_name__"
, "description": "__MSG_extension_description__"
"name": "__MSG_extensionName__"
, "description": "__MSG_extensionDescription__"
, "version": "EXTENSION_VERSION"
, "applications": {

View File

@@ -122,7 +122,7 @@ async function onRequestSessionSuccess (session_) {
session = session_;
let mediaUrl = new URL(srcUrl);
const port = options.option_localMediaServerPort;
const port = options.localMediaServerPort;
if (isLocalFile) {
await new Promise((resolve, reject) => {
@@ -295,7 +295,7 @@ window.__onGCastApiAvailable = async function (loaded, errorInfo) {
options = (await browser.storage.sync.get("options")).options;
if (isLocalFile && !options.option_localMediaEnabled) {
if (isLocalFile && !options.localMediaEnabled) {
logMessage("Local media casting not enabled");
return;
}

View File

@@ -0,0 +1,175 @@
import React, { Component } from "react";
import EditableListItem from "./EditableListItem";
const _ = browser.i18n.getMessage;
export default class EditableList extends Component {
constructor (props) {
super(props);
this.state = {
items: new Set(this.props.data)
, addingNewItem: false
, rawView: false
, rawViewValue: ""
};
this.handleItemRemove = this.handleItemRemove.bind(this);
this.handleItemEdit = this.handleItemEdit.bind(this);
this.handleSwitchView = this.handleSwitchView.bind(this);
this.handleSaveRaw = this.handleSaveRaw.bind(this);
this.handleRawViewTextAreaChange = this.handleRawViewTextAreaChange.bind(this);
this.handleAddItem = this.handleAddItem.bind(this);
this.handleNewItemRemove = this.handleNewItemRemove.bind(this);
this.handleNewItemEdit = this.handleNewItemEdit.bind(this);
}
handleItemRemove (item) {
this.setState(currentState => {
const newItems = new Set(currentState.items);
newItems.delete(item);
return {
items: newItems
};
}, () => {
this.props.onChange(Array.from(this.state.items));
});
}
handleItemEdit (item, newValue) {
this.setState(currentState => ({
items: new Set([...currentState.items]
.map(item_ => item_ === item ? newValue : item_))
}), () => {
this.props.onChange(Array.from(this.state.items));
});
}
handleSwitchView () {
this.setState(currentState => {
if (currentState.rawView) {
return {
rawView: false
, rawViewValue: ""
};
}
return {
rawView: true
, rawViewValue: [...currentState.items.values()].join("\n")
};
});
}
handleSaveRaw () {
this.setState(currentState => {
console.log(currentState.rawViewValue);
const newItems = currentState.rawViewValue.split("\n")
.filter(item => item !== "");
if ("itemPattern" in this.props) {
for (const item of newItems) {
if (!this.props.itemPattern.test(item)) {
this.rawViewTextArea.setCustomValidity(
this.props.itemPatternError(item));
return;
}
}
this.rawViewTextArea.setCustomValidity("");
}
return {
items: new Set(newItems)
};
}, () => {
this.props.onChange(Array.from(this.state.items));
});
}
handleRawViewTextAreaChange (ev) {
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
}
this.setState({
rawViewValue: ev.target.value
});
}
handleAddItem () {
this.setState({
addingNewItem: true
});
}
handleNewItemRemove () {
this.setState({
addingNewItem: false
});
}
handleNewItemEdit (item, newItem) {
this.setState(currentState => ({
items: [ ...currentState.items, newItem ]
, addingNewItem: false
}), () => {
this.props.onChange(Array.from(this.state.items));
});
}
render () {
const items = Array.from(this.state.items.values());
return (
<div className="editable-list">
<button className="editable-list__view-button"
onClick={ this.handleSwitchView }>
{ this.state.rawView
? _("optionsUserAgentWhitelistBasicView")
: _("optionsUserAgentWhitelistRawView") }
</button>
{ this.state.rawView &&
<button className="editable-list__save-raw-button"
onClick={ this.handleSaveRaw }>
{ _("optionsUserAgentWhitelistSaveRaw") }
</button> }
<hr />
{ do {
if (this.state.rawView) {
<textarea className="editable-list__raw-view"
rows={ items.length}
value={ this.state.rawViewValue}
onChange={ this.handleRawViewTextAreaChange }
ref={ el => { this.rawViewTextArea = el }}>
</textarea>
} else {
<ul className="editable-list__items">
{ items.map((item, i) =>
<EditableListItem text={ item }
itemPattern={ this.props.itemPattern }
itemPatternError={ this.props.itemPatternError }
onRemove={ this.handleItemRemove }
onEdit={ this.handleItemEdit }
key={ i } /> )}
{ this.state.addingNewItem &&
<EditableListItem text=""
itemPattern={ this.props.itemPattern }
itemPatternError={ this.props.itemPatternError }
onRemove={ this.handleNewItemRemove }
onEdit={ this.handleNewItemEdit }
editing={ true } /> }
<div className="editable-list__item editable-list__item-actions">
<button className="editable-list__add-button"
onClick={ this.handleAddItem }>
{ _("optionsUserAgentWhitelistAddItem") }
</button>
</div>
</ul>
}
}}
</div>
);
}
}

View File

@@ -0,0 +1,91 @@
import React, { Component } from "react";
const _ = browser.i18n.getMessage;
export default class EditableListItem extends Component {
constructor (props) {
super(props);
this.state = {
editing: this.props.editing || false
, editValue: ""
};
this.handleRemove = this.handleRemove.bind(this);
this.handleEditBegin = this.handleEditBegin.bind(this);
this.handleEditEnd = this.handleEditEnd.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
}
handleRemove () {
this.props.onRemove(this.props.text);
}
handleEditBegin () {
this.setState({
editing: true
, editValue: this.props.text
});
}
handleEditEnd (ev) {
if (this.props.editing
&& !this.props.itemPattern.test(this.state.editValue)) {
ev.target.setCustomValidity(this.props.itemPatternError());
}
if (!ev.target.validity.valid) {
return;
}
this.props.onEdit(this.props.text, this.state.editValue);
this.setState({
editing: false
, editValue: ""
});
}
handleInputChange (ev) {
this.setState({
editValue: ev.target.value
});
if (!this.props.itemPattern.test(ev.target.value)) {
ev.target.setCustomValidity(this.props.itemPatternError());
} else {
ev.target.setCustomValidity("");
}
}
handleInputKeyPress (ev) {
if (ev.key === "Enter") {
this.handleEditEnd({ target: ev.target });
}
}
render () {
return (
<li className="editable-list__item">
<div className="editable-list__title"
onDoubleClick={ this.handleEditBegin }>
{ this.state.editing
? <input className="editable-list__edit-field"
type="text"
autoFocus
value={ this.state.editValue }
onBlur={ this.handleEditEnd }
onChange={ this.handleInputChange }
onKeyPress={ this.handleInputKeyPress }/>
: this.props.text }
</div>
<button onClick={ this.handleEditBegin }>
{ _("optionsUserAgentWhitelistEditItem") }
</button>
<button onClick={ this.handleRemove }>
{ _("optionsUserAgentWhitelistRemoveItem") }
</button>
</li>
);
}
}

View File

@@ -1,11 +1,11 @@
export default {
option_mediaEnabled: true
, option_localMediaEnabled: true
, option_localMediaServerPort: 9555
, option_uaWhitelistEnabled: true
, option_uaWhitelist: [
mediaEnabled: true
, localMediaEnabled: true
, localMediaServerPort: 9555
, mirroringEnabled: false
, mirroringAppId: MIRRORING_APP_ID
, userAgentWhitelistEnabled: true
, userAgentWhitelist: [
"https://www.netflix.com/*"
]
, option_mirroringEnabled: false
, option_mirroringAppId: MIRRORING_APP_ID
}

View File

@@ -4,8 +4,8 @@
grid-column-gap: 20px;
grid-row-gap: 5px;
}
.category-name {}
.category-description,
.category__name {}
.category__description,
.category .category {
color: graytext;
grid-column: span 2;
@@ -15,7 +15,7 @@
display: contents;
}
.option-label {
.option__label {
text-align: right;
display: inline-block;
}

View File

@@ -1,9 +1,10 @@
"use strict";
import React, { Component } from "react";
import ReactDOM from "react-dom";
import ReactDOM from "react-dom";
import defaultOptions from "./defaultOptions";
import defaultOptions from "./defaultOptions";
import EditableList from "./EditableList";
const _ = browser.i18n.getMessage;
@@ -11,266 +12,24 @@ const _ = browser.i18n.getMessage;
const MATCH_PATTERN_REGEX = /^(?:(?:(\*|https?|ftp):\/\/((?:\*\.|[^\/\*])+)|(file):\/\/\/?(?:\*\.|[^\/\*])+)(\/.*)|<all_urls>)$/;
class EditableListItem extends React.Component {
constructor (props) {
super(props);
this.state = {
editing: this.props.editing || false
, editValue: ""
};
function getInputValue (input) {
switch (input.type) {
case "checkbox":
return input.checked;
case "number":
return parseFloat(input.value);
this.handleRemove = this.handleRemove.bind(this);
this.handleEditBegin = this.handleEditBegin.bind(this);
this.handleEditEnd = this.handleEditEnd.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
}
handleRemove () {
this.props.onRemove(this.props.text);
}
handleEditBegin () {
this.setState({
editing: true
, editValue: this.props.text
});
}
handleEditEnd (ev) {
if (this.props.editing
&& !this.props.itemPattern.test(this.state.editValue)) {
ev.target.setCustomValidity(this.props.itemPatternError());
}
if (!ev.target.validity.valid) {
return;
}
this.props.onEdit(this.props.text, this.state.editValue);
this.setState({
editing: false
, editValue: ""
});
}
handleInputChange (ev) {
this.setState({
editValue: ev.target.value
});
if (!this.props.itemPattern.test(ev.target.value)) {
ev.target.setCustomValidity(this.props.itemPatternError());
} else {
ev.target.setCustomValidity("");
}
}
handleInputKeyPress (ev) {
if (ev.key === "Enter") {
this.handleEditEnd({ target: ev.target });
}
}
render () {
return (
<li className="editable-list__item">
<div className="editable-list__title"
onDoubleClick={ this.handleEditBegin }>
{ this.state.editing
? <input className="editable-list__edit-field"
type="text"
autoFocus
value={ this.state.editValue }
onBlur={ this.handleEditEnd }
onChange={ this.handleInputChange }
onKeyPress={ this.handleInputKeyPress }/>
: this.props.text }
</div>
<button onClick={ this.handleEditBegin }>
{ _("options_option_uaWhitelistItemEdit") }
</button>
<button onClick={ this.handleRemove }>
{ _("options_option_uaWhitelistItemRemove") }
</button>
</li>
);
default:
return input.value;
}
}
class EditableList extends React.Component {
constructor (props) {
super(props);
this.state = {
items: new Set(this.props.data)
, addingNewItem: false
, rawView: false
, rawViewValue: ""
};
this.handleItemRemove = this.handleItemRemove.bind(this);
this.handleItemEdit = this.handleItemEdit.bind(this);
this.handleSwitchView = this.handleSwitchView.bind(this);
this.handleSaveRaw = this.handleSaveRaw.bind(this);
this.handleRawViewTextAreaChange = this.handleRawViewTextAreaChange.bind(this);
this.handleAddItem = this.handleAddItem.bind(this);
this.handleNewItemRemove = this.handleNewItemRemove.bind(this);
this.handleNewItemEdit = this.handleNewItemEdit.bind(this);
}
handleItemRemove (item) {
this.setState(currentState => {
const newItems = new Set(currentState.items);
newItems.delete(item);
return {
items: newItems
};
}, () => {
this.props.onListChange(Array.from(this.state.items));
});
}
handleItemEdit (item, newValue) {
this.setState(currentState => ({
items: new Set([...currentState.items]
.map(item_ => item_ === item ? newValue : item_))
}), () => {
this.props.onListChange(Array.from(this.state.items));
});
}
handleSwitchView () {
this.setState(currentState => {
if (currentState.rawView) {
return {
rawView: false
, rawViewValue: ""
};
}
return {
rawView: true
, rawViewValue: [...currentState.items.values()].join("\n")
};
});
}
handleSaveRaw () {
this.setState(currentState => {
console.log(currentState.rawViewValue);
const newItems = currentState.rawViewValue.split("\n")
.filter(item => item !== "");
if ("itemPattern" in this.props) {
for (const item of newItems) {
if (!this.props.itemPattern.test(item)) {
this.rawViewTextArea.setCustomValidity(
this.props.itemPatternError(item));
return;
}
}
this.rawViewTextArea.setCustomValidity("");
}
return {
items: new Set(newItems)
};
}, () => {
this.props.onListChange(Array.from(this.state.items));
});
}
handleRawViewTextAreaChange (ev) {
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
}
this.setState({
rawViewValue: ev.target.value
});
}
handleAddItem () {
this.setState({
addingNewItem: true
});
}
handleNewItemRemove () {
this.setState({
addingNewItem: false
});
}
handleNewItemEdit (item, newItem) {
this.setState(currentState => ({
items: [ ...currentState.items, newItem ]
, addingNewItem: false
}), () => {
this.props.onListChange(Array.from(this.state.items));
});
}
render () {
const items = Array.from(this.state.items.values());
return (
<div className="editable-list">
<button className="editable-list__view-button"
onClick={ this.handleSwitchView }>
{ this.state.rawView
? _("options_option_uaWhitelistBasicView")
: _("options_option_uaWhitelistRawView") }
</button>
{ this.state.rawView &&
<button className="editable-list__save-raw-button"
onClick={ this.handleSaveRaw }>
{ _("options_option_uaWhitelistSaveRaw") }
</button> }
<hr />
{
this.state.rawView
? ( <textarea className="editable-list__raw-view"
rows={ items.length}
value={ this.state.rawViewValue}
onChange={ this.handleRawViewTextAreaChange }
ref={ el => { this.rawViewTextArea = el }}>
</textarea> )
: ( <ul className="editable-list__items">
{ items.map((item, i) =>
<EditableListItem text={ item }
itemPattern={ this.props.itemPattern }
itemPatternError={ this.props.itemPatternError }
onRemove={ this.handleItemRemove }
onEdit={ this.handleItemEdit }
key={ i } /> )}
{ this.state.addingNewItem &&
<EditableListItem text=""
itemPattern={ this.props.itemPattern }
itemPatternError={ this.props.itemPatternError }
onRemove={ this.handleNewItemRemove }
onEdit={ this.handleNewItemEdit }
editing={ true } /> }
<div className="editable-list__item editable-list__item-actions">
<button className="editable-list__add-button"
onClick={ this.handleAddItem }>
{ _("options_option_uaWhitelistAddItem") }
</button>
</div>
</ul> )
}
</div>
);
}
}
class OptionsApp extends Component {
class App extends Component {
constructor (props) {
super(props);
this.state = {
...props.options
options: props.options
, isFormValid: true
};
@@ -278,8 +37,10 @@ class OptionsApp extends Component {
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleFormChange = this.handleFormChange.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleListChange = this.handleListChange.bind(this);
this.getItemPatternError = this.getItemPatternError.bind(this);
this.handleWhitelistChange = this.handleWhitelistChange.bind(this);
this.getWhitelistItemPatternError
= this.getWhitelistItemPatternError.bind(this);
}
/**
@@ -287,21 +48,13 @@ class OptionsApp extends Component {
*/
setStorage () {
return browser.storage.sync.set({
options: {
option_mediaEnabled: this.state.option_mediaEnabled
, option_localMediaEnabled: this.state.option_localMediaEnabled
, option_localMediaServerPort: this.state.option_localMediaServerPort
, option_uaWhitelistEnabled: this.state.option_uaWhitelistEnabled
, option_uaWhitelist: this.state.option_uaWhitelist
, option_mirroringEnabled: this.state.option_mirroringEnabled
, option_mirroringAppId: this.state.option_mirroringAppId
}
options: this.state.options
});
}
handleReset () {
this.setState({
...defaultOptions
options: defaultOptions
});
// TODO: Propagate state properly
@@ -314,7 +67,8 @@ class OptionsApp extends Component {
this.form.reportValidity();
try {
const { options: oldOptions } = await browser.storage.sync.get("options");
const { options: oldOptions }
= await browser.storage.sync.get("options");
await this.setStorage();
const { options } = await browser.storage.sync.get("options");
@@ -341,31 +95,24 @@ class OptionsApp extends Component {
});
}
handleInputChange (ev) {
const val = do {
if (ev.target.type === "checkbox") {
ev.target.checked;
} else if (ev.target.type === "number") {
parseInt(ev.target.value);
} else {
ev.target.value;
}
};
const { target } = ev;
this.setState({
[ ev.target.name ]: val
this.setState(({ options }) => {
options[target.name] = getInputValue(target);
return { options };
});
}
handleListChange (data) {
this.setState({
option_uaWhitelist: data
handleWhitelistChange (whitelist) {
this.setState(({ options }) => {
options.userAgentWhitelist = whitelist;
return { options };
});
}
getItemPatternError (info) {
return _("options_option_uaWhitelistInvalidMatchPattern", info);
getWhitelistItemPatternError (info) {
return _("optionsUserAgentWhitelistInvalidMatchPattern", info);
}
render () {
@@ -374,122 +121,122 @@ class OptionsApp extends Component {
onSubmit={ this.handleFormSubmit }
onChange={ this.handleFormChange }>
<fieldset className="category">
<legend className="category-name">
{ _("options_category_media") }
<legend className="category__name">
{ _("optionsMediaCategoryName") }
</legend>
<p className="category-description">
{ _("options_category_media_description") }
<p className="category__description">
{ _("optionsMediaCategoryDescription") }
</p>
<label className="option">
<div className="option-label">
{ _("options_option_mediaEnabled") }
<div className="option__label">
{ _("optionsMediaEnabled") }
</div>
<input name="option_mediaEnabled"
<input name="mediaEnabled"
type="checkbox"
checked={ this.state.option_mediaEnabled }
checked={ this.state.options.mediaEnabled }
onChange={ this.handleInputChange } />
</label>
<fieldset className="category">
<legend className="category-name">
{ _("options_category_localMedia") }
<legend className="category__name">
{ _("optionsLocalMediaCategoryName") }
</legend>
<p className="category-description">
{ _("options_category_localMedia_description") }
<p className="category__description">
{ _("optionsLocalMediaCategoryDescription") }
</p>
<label className="option">
<div className="option-label">
{ _("options_option_localMediaEnabled") }
<div className="option__label">
{ _("optionsLocalMediaEnabled") }
</div>
<input name="option_localMediaEnabled"
<input name="localMediaEnabled"
type="checkbox"
checked={ this.state.option_localMediaEnabled }
checked={ this.state.options.localMediaEnabled }
onChange={ this.handleInputChange } />
</label>
<label className="option">
<div className="option-label">
{ _("options_option_localMediaServerPort") }
<div className="option__label">
{ _("optionsLocalMediaServerPort") }
</div>
<input name="option_localMediaServerPort"
<input name="localMediaServerPort"
type="number"
required
min="1025"
max="65535"
value={ this.state.option_localMediaServerPort }
value={ this.state.options.localMediaServerPort }
onChange={ this.handleInputChange } />
</label>
</fieldset>
</fieldset>
<fieldset className="category">
<legend className="category-name">
{ _("options_category_mirroring") }
<legend className="category__name">
{ _("optionsMirroringCategoryName") }
</legend>
<p className="category-description">
{ _("options_category_mirroring_description") }
<p className="category__description">
{ _("optionsMirroringCategoryDescription") }
</p>
<label className="option">
<div className="option-label">
{ _("options_option_mirroringEnabled") }
<div className="option__label">
{ _("optionsMirroringEnabled") }
</div>
<input name="option_mirroringEnabled"
<input name="mirroringEnabled"
type="checkbox"
checked={ this.state.option_mirroringEnabled }
checked={ this.state.options.mirroringEnabled }
onChange={ this.handleInputChange } />
</label>
<label className="option">
<div className="option-label">
{ _("options_option_mirroringAppId") }
<div className="option__label">
{ _("optionsMirroringAppId") }
</div>
<input name="option_mirroringAppId"
<input name="mirroringAppId"
type="text"
required
value={ this.state.option_mirroringAppId }
value={ this.state.options.mirroringAppId }
onChange={ this.handleInputChange } />
</label>
</fieldset>
<fieldset className="category">
<legend className="category-name">
{ _("options_category_uaWhitelist") }
<legend className="category__name">
{ _("optionsUserAgentWhitelistCategoryName") }
</legend>
<p className="category-description">
{ _("options_category_uaWhitelist_description") }
<p className="category__description">
{ _("optionsUserAgentWhitelistCategoryDescription") }
</p>
<label className="option">
<div className="option-label">
{ _("options_option_uaWhitelistEnabled") }
<div className="option__label">
{ _("optionsUserAgentWhitelistEnabled") }
</div>
<input name="option_uaWhitelistEnabled"
<input name="userAgentWhitelistEnabled"
type="checkbox"
checked={ this.state.option_uaWhitelistEnabled }
checked={ this.state.options.userAgentWhitelistEnabled }
onChange={ this.handleInputChange } />
</label>
<label className="option">
<div className="option-label">
{ _("options_option_uaWhitelist") }
<div className="option">
<div className="option__label">
{ _("optionsUserAgentWhitelistContent") }
</div>
<EditableList data={ this.state.option_uaWhitelist }
onListChange={ this.handleListChange }
<EditableList data={ this.state.options.userAgentWhitelist }
onChange={ this.handleWhitelistChange }
itemPattern={ MATCH_PATTERN_REGEX }
itemPatternError={ this.getItemPatternError }/>
</label>
itemPatternError={ this.getWhitelistItemPatternError }/>
</div>
</fieldset>
<div id="buttons">
<button onClick={ this.handleReset }>
{ _("options_reset") }
{ _("optionsReset") }
</button>
<button type="submit"
disabled={ !this.state.isFormValid }>
{ _("options_submit") }
{ _("optionsSubmit") }
</button>
</div>
</form>
@@ -500,6 +247,6 @@ class OptionsApp extends Component {
browser.storage.sync.get("options").then(({options}) => {
ReactDOM.render(
<OptionsApp options={options} />
<App options={options} />
, document.querySelector("#root"));
});

View File

@@ -87,7 +87,9 @@ class App extends Component {
<div>
<div className="media-select">
Cast
<select value={this.state.selectedMedia} onChange={this.onSelectChange.bind(this)} className="media-select-dropdown">
<select value={this.state.selectedMedia}
onChange={this.onSelectChange.bind(this)}
className="media-select-dropdown">
<option value="app" disabled={shareMedia}>this site's app</option>
<option value="tab" disabled={!shareMedia}>Tab</option>
<option value="screen" disabled={!shareMedia}>Screen</option>
@@ -152,12 +154,12 @@ class Receiver extends Component {
disabled={this.props.isLoading}>
{ do {
if (this.state.isLoading) {
_("popup_casting_button_label") +
_("popupCastingButtonLabel") +
(this.state.isLoading
? this.state.ellipsis
: "" )
} else {
_("popup_cast_button_label")
_("popupCastButtonLabel")
}
}}
</button>

View File

@@ -56,6 +56,9 @@ module.exports = (env) => ({
rules: [
{
test: /\.jsx?$/
, resolve: {
extensions: [ ".js", ".jsx" ]
}
, include: `${includePath}`
, use: {
loader: "babel-loader"