Bug 1517596 - Add basic DiscoveryStream devtools
This commit is contained in:
Родитель
4ec4f4bb06
Коммит
9220cb8e28
|
@ -41,6 +41,8 @@ for (const type of [
|
|||
"DIALOG_OPEN",
|
||||
"DISCOVERY_STREAM_CONFIG_CHANGE",
|
||||
"DISCOVERY_STREAM_CONFIG_SETUP",
|
||||
"DISCOVERY_STREAM_CONFIG_SET_VALUE",
|
||||
"DISCOVERY_STREAM_LAYOUT_RESET",
|
||||
"DISCOVERY_STREAM_LAYOUT_UPDATE",
|
||||
"DOWNLOAD_CHANGED",
|
||||
"FILL_SEARCH_TERM",
|
||||
|
|
|
@ -52,6 +52,10 @@ const INITIAL_STATE = {
|
|||
// This is a JSON-parsed copy of the discoverystream.config pref value.
|
||||
config: {enabled: false, layout_endpoint: ""},
|
||||
layout: [],
|
||||
lastUpdated: null,
|
||||
feeds: {
|
||||
// "https://foo.com/feed1": {lastUpdated: 123, data: []}
|
||||
},
|
||||
},
|
||||
Search: {
|
||||
// Pretend the search box is focused after handing off to AwesomeBar.
|
||||
|
@ -448,7 +452,9 @@ function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) {
|
|||
case at.DISCOVERY_STREAM_CONFIG_SETUP:
|
||||
return {...prevState, config: action.data || {}};
|
||||
case at.DISCOVERY_STREAM_LAYOUT_UPDATE:
|
||||
return {...prevState, layout: action.data || []};
|
||||
return {...prevState, lastUpdated: action.data.lastUpdated || null, layout: action.data.layout || []};
|
||||
case at.DISCOVERY_STREAM_LAYOUT_RESET:
|
||||
return {...prevState, lastUpdated: INITIAL_STATE.DiscoveryStream.lastUpdated, layout: INITIAL_STATE.DiscoveryStream.layout};
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,57 @@
|
|||
import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
|
||||
import {ASRouterUtils} from "../../asrouter/asrouter-content";
|
||||
import {connect} from "react-redux";
|
||||
import {ModalOverlay} from "../../asrouter/components/ModalOverlay/ModalOverlay";
|
||||
import React from "react";
|
||||
import {SimpleHashRouter} from "./SimpleHashRouter";
|
||||
|
||||
const Row = props => (<tr className="message-item" {...props}>{props.children}</tr>);
|
||||
|
||||
function relativeTime(timestamp) {
|
||||
if (!timestamp) {
|
||||
return "";
|
||||
}
|
||||
const seconds = Math.floor((Date.now() - timestamp) / 1000);
|
||||
const minutes = Math.floor((Date.now() - timestamp) / 60000);
|
||||
if (seconds < 2) {
|
||||
return "just now";
|
||||
} else if (seconds < 60) {
|
||||
return `${seconds} seconds ago`;
|
||||
} else if (minutes === 1) {
|
||||
return "1 minute ago";
|
||||
} else if (minutes < 600) {
|
||||
return `${minutes} minutes ago`;
|
||||
}
|
||||
return new Date(timestamp).toLocaleString();
|
||||
}
|
||||
|
||||
class DiscoveryStreamAdmin extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onEnableToggle = this.onEnableToggle.bind(this);
|
||||
}
|
||||
|
||||
setConfigValue(name, value) {
|
||||
this.props.dispatch(ac.OnlyToMain({type: at.DISCOVERY_STREAM_CONFIG_SET_VALUE, data: {name, value}}));
|
||||
}
|
||||
|
||||
onEnableToggle(event) {
|
||||
this.setConfigValue("enabled", event.target.checked);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {config, lastUpdated} = this.props.state;
|
||||
return (<div>
|
||||
<div className="dsEnabled"><input type="checkbox" checked={config.enabled} onChange={this.onEnableToggle} /> enabled</div>
|
||||
|
||||
<table style={config.enabled ? null : {opacity: 0.5}}><tbody>
|
||||
<Row><td className="min">Data last fetched</td><td>{relativeTime(lastUpdated) || "(no data)"}</td></Row>
|
||||
<Row><td className="min">Endpoint</td><td>{config.layout_endpoint || "(empty)"}</td></Row>
|
||||
</tbody></table>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export class ASRouterAdminInner extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -393,6 +441,17 @@ export class ASRouterAdminInner extends React.PureComponent {
|
|||
</tbody></table>);
|
||||
}
|
||||
|
||||
renderDiscoveryStream() {
|
||||
const {config} = this.props.DiscoveryStream;
|
||||
|
||||
return (<div>
|
||||
<table><tbody>
|
||||
<tr className="message-item"><td className="min">Enabled</td><td>{config.enabled ? "yes" : "no"}</td></tr>
|
||||
<tr className="message-item"><td className="min">Endpoint</td><td>{config.endpoint || "(empty)"}</td></tr>
|
||||
</tbody></table>
|
||||
</div>);
|
||||
}
|
||||
|
||||
renderAttributionParamers() {
|
||||
return (
|
||||
<div>
|
||||
|
@ -433,9 +492,14 @@ export class ASRouterAdminInner extends React.PureComponent {
|
|||
<h2>Pocket</h2>
|
||||
{this.renderPocketStories()}
|
||||
</React.Fragment>);
|
||||
case "ds":
|
||||
return (<React.Fragment>
|
||||
<h2>Discovery Stream</h2>
|
||||
<DiscoveryStreamAdmin state={this.props.DiscoveryStream} dispatch={this.props.dispatch} />
|
||||
</React.Fragment>);
|
||||
default:
|
||||
return (<React.Fragment>
|
||||
<h2>Message Providers <button title="Restore all provider settings that ship with Firefox" className="button" onClick={this.resetPref}>Restorear default prefs</button></h2>
|
||||
<h2>Message Providers <button title="Restore all provider settings that ship with Firefox" className="button" onClick={this.resetPref}>Restore default prefs</button></h2>
|
||||
{this.state.providers ? this.renderProviders() : null}
|
||||
<h2>Messages</h2>
|
||||
{this.renderMessageFilter()}
|
||||
|
@ -452,6 +516,7 @@ export class ASRouterAdminInner extends React.PureComponent {
|
|||
<li><a href="#devtools">General</a></li>
|
||||
<li><a href="#devtools-targeting">Targeting</a></li>
|
||||
<li><a href="#devtools-pocket">Pocket</a></li>
|
||||
<li><a href="#devtools-ds">Discovery Stream</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<main className="main-panel">
|
||||
|
@ -472,4 +537,4 @@ export class ASRouterAdminInner extends React.PureComponent {
|
|||
}
|
||||
|
||||
export const _ASRouterAdmin = props => (<SimpleHashRouter><ASRouterAdminInner {...props} /></SimpleHashRouter>);
|
||||
export const ASRouterAdmin = connect(state => ({Sections: state.Sections}))(_ASRouterAdmin);
|
||||
export const ASRouterAdmin = connect(state => ({Sections: state.Sections, DiscoveryStream: state.DiscoveryStream}))(_ASRouterAdmin);
|
||||
|
|
|
@ -139,4 +139,12 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.dsEnabled {
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import React from "react";
|
|||
import {Search} from "content-src/components/Search/Search";
|
||||
import {Sections} from "content-src/components/Sections/Sections";
|
||||
|
||||
let didLogDevtoolsHelpText = false;
|
||||
|
||||
const PrefsButton = injectIntl(props => (
|
||||
<div className="prefs-button">
|
||||
<button className="icon icon-settings" onClick={props.onClick} title={props.intl.formatMessage({id: "settings_pane_button_label"})} />
|
||||
|
@ -86,8 +88,10 @@ export class _Base extends React.PureComponent {
|
|||
if (window.location.hash.startsWith("#asrouter") ||
|
||||
window.location.hash.startsWith("#devtools")) {
|
||||
return (<ASRouterAdmin />);
|
||||
} else if (!didLogDevtoolsHelpText) {
|
||||
console.log("Activity Stream devtools enabled. To access visit %cabout:newtab#devtools", "font-weight: bold"); // eslint-disable-line no-console
|
||||
didLogDevtoolsHelpText = true;
|
||||
}
|
||||
console.log("Activity Stream devtools enabled. To access visit %cabout:newtab#devtools", "font-weight: bold"); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
if (!props.isPrerendered && !initialized) {
|
||||
|
|
|
@ -82,7 +82,6 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
|
||||
async loadCachedData() {
|
||||
const cachedData = await this.cache.get() || {};
|
||||
|
||||
let {layout: layoutResponse} = cachedData;
|
||||
if (!layoutResponse || !(Date.now() - layoutResponse._timestamp < LAYOUT_UPDATE_TIME)) {
|
||||
layoutResponse = await this.fetchLayout();
|
||||
|
@ -95,7 +94,13 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
}
|
||||
|
||||
if (layoutResponse && layoutResponse.layout) {
|
||||
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: layoutResponse.layout}));
|
||||
this.store.dispatch(ac.BroadcastToContent({
|
||||
type: at.DISCOVERY_STREAM_LAYOUT_UPDATE,
|
||||
data: {
|
||||
layout: layoutResponse.layout,
|
||||
lastUpdated: layoutResponse._timestamp,
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +112,7 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
async disable() {
|
||||
await this.clearCache();
|
||||
// Reset reducer
|
||||
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: []}));
|
||||
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_LAYOUT_RESET}));
|
||||
this.loaded = false;
|
||||
}
|
||||
|
||||
|
@ -140,6 +145,9 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
await this.enable();
|
||||
}
|
||||
break;
|
||||
case at.DISCOVERY_STREAM_CONFIG_SET_VALUE:
|
||||
Services.prefs.setStringPref(CONFIG_PREF_NAME, JSON.stringify({...this.config, [action.data.name]: action.data.value}));
|
||||
break;
|
||||
case at.DISCOVERY_STREAM_CONFIG_CHANGE:
|
||||
// When the config pref changes, load or unload data as needed.
|
||||
await this.onPrefChange();
|
||||
|
|
|
@ -660,8 +660,9 @@ describe("Reducers", () => {
|
|||
assert.equal(DiscoveryStream(undefined, {type: "some_action"}), INITIAL_STATE.DiscoveryStream);
|
||||
});
|
||||
it("should set layout data with DISCOVERY_STREAM_LAYOUT_UPDATE", () => {
|
||||
const state = DiscoveryStream(undefined, {type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: ["test"]});
|
||||
const state = DiscoveryStream(undefined, {type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: {layout: ["test"], lastUpdated: 123}});
|
||||
assert.equal(state.layout[0], "test");
|
||||
assert.equal(state.lastUpdated, 123);
|
||||
});
|
||||
it("should set config data with DISCOVERY_STREAM_CONFIG_CHANGE", () => {
|
||||
const state = DiscoveryStream(undefined, {type: at.DISCOVERY_STREAM_CONFIG_CHANGE, data: {enabled: true}});
|
||||
|
|
|
@ -117,6 +117,17 @@ describe("DiscoveryStreamFeed", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#onAction: DISCOVERY_STREAM_CONFIG_SET_VALUE", () => {
|
||||
it("should add the new value to the pref without changing the existing values", async () => {
|
||||
sandbox.stub(global.Services.prefs, "setStringPref");
|
||||
configPrefStub.returns(JSON.stringify({enabled: true}));
|
||||
|
||||
await feed.onAction({type: at.DISCOVERY_STREAM_CONFIG_SET_VALUE, data: {name: "layout_endpoint", value: "foo.com"}});
|
||||
|
||||
assert.calledWith(global.Services.prefs.setStringPref, CONFIG_PREF_NAME, JSON.stringify({enabled: true, layout_endpoint: "foo.com"}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#onAction: DISCOVERY_STREAM_CONFIG_CHANGE", () => {
|
||||
it("should call this.loadCachedData if config.enabled changes to true ", async () => {
|
||||
sandbox.stub(feed.cache, "set").returns(Promise.resolve());
|
||||
|
|
Загрузка…
Ссылка в новой задаче