App.js (6793B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 connect, 9 } = require("resource://devtools/client/shared/vendor/react-redux.js"); 10 const { 11 createFactory, 12 PureComponent, 13 } = require("resource://devtools/client/shared/vendor/react.mjs"); 14 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 15 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 16 17 const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js"); 18 const Localized = createFactory(FluentReact.Localized); 19 20 const Route = createFactory( 21 require("resource://devtools/client/shared/vendor/react-router-dom.js").Route 22 ); 23 const Switch = createFactory( 24 require("resource://devtools/client/shared/vendor/react-router-dom.js").Switch 25 ); 26 const Redirect = createFactory( 27 require("resource://devtools/client/shared/vendor/react-router-dom.js") 28 .Redirect 29 ); 30 31 const Types = require("resource://devtools/client/aboutdebugging/src/types/index.js"); 32 const { 33 PAGE_TYPES, 34 RUNTIMES, 35 } = require("resource://devtools/client/aboutdebugging/src/constants.js"); 36 37 const ConnectPage = createFactory( 38 require("resource://devtools/client/aboutdebugging/src/components/connect/ConnectPage.js") 39 ); 40 const RuntimePage = createFactory( 41 require("resource://devtools/client/aboutdebugging/src/components/RuntimePage.js") 42 ); 43 const Sidebar = createFactory( 44 require("resource://devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js") 45 ); 46 47 class App extends PureComponent { 48 static get propTypes() { 49 return { 50 adbAddonStatus: Types.adbAddonStatus, 51 // The "dispatch" helper is forwarded to the App component via connect. 52 // From that point, components are responsible for forwarding the dispatch 53 // property to all components who need to dispatch actions. 54 dispatch: PropTypes.func.isRequired, 55 // getString prop is injected by the withLocalization wrapper 56 getString: PropTypes.func.isRequired, 57 isAdbReady: PropTypes.bool.isRequired, 58 isScanningUsb: PropTypes.bool.isRequired, 59 networkLocations: PropTypes.arrayOf(Types.location).isRequired, 60 networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired, 61 selectedPage: Types.page, 62 selectedRuntimeId: PropTypes.string, 63 usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired, 64 }; 65 } 66 67 componentDidUpdate() { 68 this.updateTitle(); 69 } 70 71 updateTitle() { 72 const { getString, selectedPage, selectedRuntimeId } = this.props; 73 74 const pageTitle = 75 selectedPage === PAGE_TYPES.RUNTIME 76 ? getString("about-debugging-page-title-runtime-page", { 77 selectedRuntimeId, 78 }) 79 : getString("about-debugging-page-title-setup-page"); 80 81 document.title = pageTitle; 82 } 83 84 renderConnect() { 85 const { adbAddonStatus, dispatch, networkLocations } = this.props; 86 87 return ConnectPage({ 88 adbAddonStatus, 89 dispatch, 90 networkLocations, 91 }); 92 } 93 94 // The `match` object here is passed automatically by the Route object. 95 // We are using it to read the route path. 96 // See react-router docs: 97 // https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/match.md 98 renderRuntime({ match }) { 99 const isRuntimeAvailable = id => { 100 const runtimes = [ 101 ...this.props.networkRuntimes, 102 ...this.props.usbRuntimes, 103 ]; 104 const runtime = runtimes.find(x => x.id === id); 105 return runtime?.runtimeDetails; 106 }; 107 108 const { dispatch } = this.props; 109 110 let runtimeId = match.params.runtimeId || RUNTIMES.THIS_FIREFOX; 111 if (match.params.runtimeId !== RUNTIMES.THIS_FIREFOX) { 112 const rawId = decodeURIComponent(match.params.runtimeId); 113 if (isRuntimeAvailable(rawId)) { 114 runtimeId = rawId; 115 } else { 116 // Also redirect to "This Firefox" if runtime is not found 117 return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` }); 118 } 119 } 120 121 // we need to pass a key so the component updates when we want to showcase 122 // a different runtime 123 return RuntimePage({ dispatch, key: runtimeId, runtimeId }); 124 } 125 126 renderRoutes() { 127 return Switch( 128 {}, 129 Route({ 130 path: "/setup", 131 render: () => this.renderConnect(), 132 }), 133 Route({ 134 path: "/runtime/:runtimeId", 135 render: routeProps => this.renderRuntime(routeProps), 136 }), 137 // default route when there's no match which includes "/" 138 // TODO: the url does not match "/" means invalid URL, 139 // in this case maybe we'd like to do something else than a redirect. 140 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1509897 141 Route({ 142 render: routeProps => { 143 const { pathname } = routeProps.location; 144 // The old about:debugging supported the following routes: 145 // about:debugging#workers, about:debugging#addons and about:debugging#tabs. 146 // Such links can still be found in external documentation pages. 147 // We redirect to This Firefox rather than the Setup Page here. 148 if ( 149 pathname === "/workers" || 150 pathname === "/addons" || 151 pathname === "/tabs" 152 ) { 153 return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` }); 154 } 155 return Redirect({ to: "/setup" }); 156 }, 157 }) 158 ); 159 } 160 161 render() { 162 const { 163 adbAddonStatus, 164 dispatch, 165 isAdbReady, 166 isScanningUsb, 167 networkRuntimes, 168 selectedPage, 169 selectedRuntimeId, 170 usbRuntimes, 171 } = this.props; 172 173 return Localized( 174 {}, 175 dom.div( 176 { className: "app" }, 177 Sidebar({ 178 adbAddonStatus, 179 className: "app__sidebar", 180 dispatch, 181 isAdbReady, 182 isScanningUsb, 183 networkRuntimes, 184 selectedPage, 185 selectedRuntimeId, 186 usbRuntimes, 187 }), 188 dom.main({ className: "app__content" }, this.renderRoutes()) 189 ) 190 ); 191 } 192 } 193 194 const mapStateToProps = state => { 195 return { 196 adbAddonStatus: state.ui.adbAddonStatus, 197 isAdbReady: state.ui.isAdbReady, 198 isScanningUsb: state.ui.isScanningUsb, 199 networkLocations: state.ui.networkLocations, 200 networkRuntimes: state.runtimes.networkRuntimes, 201 selectedPage: state.ui.selectedPage, 202 selectedRuntimeId: state.runtimes.selectedRuntimeId, 203 usbRuntimes: state.runtimes.usbRuntimes, 204 }; 205 }; 206 207 const mapDispatchToProps = dispatch => ({ 208 dispatch, 209 }); 210 211 module.exports = FluentReact.withLocalization( 212 connect(mapStateToProps, mapDispatchToProps)(App) 213 );