/* 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/. */ "use strict"; const { connect, } = require("resource://devtools/client/shared/vendor/react-redux.js"); const { createFactory, PureComponent, } = require("resource://devtools/client/shared/vendor/react.js"); const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js"); const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js"); const Localized = createFactory(FluentReact.Localized); const Route = createFactory( require("resource://devtools/client/shared/vendor/react-router-dom.js").Route ); const Switch = createFactory( require("resource://devtools/client/shared/vendor/react-router-dom.js").Switch ); const Redirect = createFactory( require("resource://devtools/client/shared/vendor/react-router-dom.js") .Redirect ); const Types = require("resource://devtools/client/aboutdebugging/src/types/index.js"); const { PAGE_TYPES, RUNTIMES, } = require("resource://devtools/client/aboutdebugging/src/constants.js"); const ConnectPage = createFactory( require("resource://devtools/client/aboutdebugging/src/components/connect/ConnectPage.js") ); const RuntimePage = createFactory( require("resource://devtools/client/aboutdebugging/src/components/RuntimePage.js") ); const Sidebar = createFactory( require("resource://devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js") ); class App extends PureComponent { static get propTypes() { return { adbAddonStatus: Types.adbAddonStatus, // The "dispatch" helper is forwarded to the App component via connect. // From that point, components are responsible for forwarding the dispatch // property to all components who need to dispatch actions. dispatch: PropTypes.func.isRequired, // getString prop is injected by the withLocalization wrapper getString: PropTypes.func.isRequired, isAdbReady: PropTypes.bool.isRequired, isScanningUsb: PropTypes.bool.isRequired, networkLocations: PropTypes.arrayOf(Types.location).isRequired, networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired, selectedPage: Types.page, selectedRuntimeId: PropTypes.string, usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired, }; } componentDidUpdate() { this.updateTitle(); } updateTitle() { const { getString, selectedPage, selectedRuntimeId } = this.props; const pageTitle = selectedPage === PAGE_TYPES.RUNTIME ? getString("about-debugging-page-title-runtime-page", { selectedRuntimeId, }) : getString("about-debugging-page-title-setup-page"); document.title = pageTitle; } renderConnect() { const { adbAddonStatus, dispatch, networkLocations } = this.props; return ConnectPage({ adbAddonStatus, dispatch, networkLocations, }); } // The `match` object here is passed automatically by the Route object. // We are using it to read the route path. // See react-router docs: // https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/match.md renderRuntime({ match }) { const isRuntimeAvailable = id => { const runtimes = [ ...this.props.networkRuntimes, ...this.props.usbRuntimes, ]; const runtime = runtimes.find(x => x.id === id); return runtime?.runtimeDetails; }; const { dispatch } = this.props; let runtimeId = match.params.runtimeId || RUNTIMES.THIS_FIREFOX; if (match.params.runtimeId !== RUNTIMES.THIS_FIREFOX) { const rawId = decodeURIComponent(match.params.runtimeId); if (isRuntimeAvailable(rawId)) { runtimeId = rawId; } else { // Also redirect to "This Firefox" if runtime is not found return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` }); } } // we need to pass a key so the component updates when we want to showcase // a different runtime return RuntimePage({ dispatch, key: runtimeId, runtimeId }); } renderRoutes() { return Switch( {}, Route({ path: "/setup", render: () => this.renderConnect(), }), Route({ path: "/runtime/:runtimeId", render: routeProps => this.renderRuntime(routeProps), }), // default route when there's no match which includes "/" // TODO: the url does not match "/" means invalid URL, // in this case maybe we'd like to do something else than a redirect. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1509897 Route({ render: routeProps => { const { pathname } = routeProps.location; // The old about:debugging supported the following routes: // about:debugging#workers, about:debugging#addons and about:debugging#tabs. // Such links can still be found in external documentation pages. // We redirect to This Firefox rather than the Setup Page here. if ( pathname === "/workers" || pathname === "/addons" || pathname === "/tabs" ) { return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` }); } return Redirect({ to: "/setup" }); }, }) ); } render() { const { adbAddonStatus, dispatch, isAdbReady, isScanningUsb, networkRuntimes, selectedPage, selectedRuntimeId, usbRuntimes, } = this.props; return Localized( {}, dom.div( { className: "app" }, Sidebar({ adbAddonStatus, className: "app__sidebar", dispatch, isAdbReady, isScanningUsb, networkRuntimes, selectedPage, selectedRuntimeId, usbRuntimes, }), dom.main({ className: "app__content" }, this.renderRoutes()) ) ); } } const mapStateToProps = state => { return { adbAddonStatus: state.ui.adbAddonStatus, isAdbReady: state.ui.isAdbReady, isScanningUsb: state.ui.isScanningUsb, networkLocations: state.ui.networkLocations, networkRuntimes: state.runtimes.networkRuntimes, selectedPage: state.ui.selectedPage, selectedRuntimeId: state.runtimes.selectedRuntimeId, usbRuntimes: state.runtimes.usbRuntimes, }; }; const mapDispatchToProps = dispatch => ({ dispatch, }); module.exports = FluentReact.withLocalization( connect(mapStateToProps, mapDispatchToProps)(App) );