Browse Source

Node status app (#618)

* Structure for app-status (bonus: starting app-template to document)

* Add steps to add a new app from the template

* Follow instructions, ensure we have something working

* README update

* Disable Wiki (for the time being)

* Split last sidebar render helper

* speellung (edit: spelling), AppProps & empty new apps (current)

* app-template -> app-123code

* Status header in-place

* Very basic distus display in place

* Overflow for hash

* app-status -> app-nodeinfo
Jaco Greeff 6 years ago
parent
commit
45e42047f3
38 changed files with 1039 additions and 124 deletions
  1. 9 0
      README.md
  2. 1 1
      jest.config.js
  3. 201 0
      packages/app-123code/LICENSE
  4. 24 0
      packages/app-123code/README.md
  5. 16 0
      packages/app-123code/package.json
  6. 3 0
      packages/app-123code/src/index.css
  7. 25 0
      packages/app-123code/src/index.tsx
  8. 7 0
      packages/app-123code/src/translate.ts
  9. 4 6
      packages/app-accounts/src/index.tsx
  10. 3 6
      packages/app-addresses/src/index.tsx
  11. 2 6
      packages/app-democracy/src/index.tsx
  12. 4 6
      packages/app-explorer/src/BlockByHash/BlockByHash.tsx
  13. 2 6
      packages/app-explorer/src/index.tsx
  14. 4 6
      packages/app-extrinsics/src/index.tsx
  15. 201 0
      packages/app-nodeinfo/LICENSE
  16. 1 0
      packages/app-nodeinfo/README.md
  17. 16 0
      packages/app-nodeinfo/package.json
  18. 69 0
      packages/app-nodeinfo/src/Peers.tsx
  19. 93 0
      packages/app-nodeinfo/src/Pending.tsx
  20. 63 0
      packages/app-nodeinfo/src/Summary.tsx
  21. 45 0
      packages/app-nodeinfo/src/index.css
  22. 88 0
      packages/app-nodeinfo/src/index.tsx
  23. 7 0
      packages/app-nodeinfo/src/translate.ts
  24. 11 0
      packages/app-nodeinfo/src/types.ts
  25. 3 6
      packages/app-rpc/src/index.tsx
  26. 0 5
      packages/app-settings/src/index.css
  27. 2 6
      packages/app-settings/src/index.tsx
  28. 2 5
      packages/app-staking/src/index.tsx
  29. 2 7
      packages/app-storage/src/index.tsx
  30. 2 6
      packages/app-toolbox/src/index.tsx
  31. 69 44
      packages/apps/src/SideBar/index.tsx
  32. 20 0
      packages/apps/src/routing/123code.ts
  33. 8 2
      packages/apps/src/routing/index.ts
  34. 20 0
      packages/apps/src/routing/nodeinfo.ts
  35. 2 6
      packages/apps/src/types.ts
  36. 2 0
      packages/apps/webpack.config.js
  37. 6 0
      packages/ui-app/src/types.ts
  38. 2 0
      tsconfig.json

+ 9 - 0
README.md

@@ -26,6 +26,7 @@ The repo is split into a number of packages, each representing an application. T
 - [app-rpc](packages/app-rpc/) Sumission of raw data to RPC endpoints.
 - [app-settings](packages/app-settings/) A basic settings management app, allowing choice of language, node to connect to, and theme
 - [app-staking](packages/app-staking/) A basic staking management app, allowing staking and nominations.
+- [app-nodeinfo](packages/app-nodeinfo/) Node information and status
 - [app-storage](packages/app-storage/) A simple node storage query application. Multiple queries can be queued and updates as new values become available.
 - [app-toolbox](packages/app-toolbox/) Utilities to manage data.
 - [app-transfer](packages/app-transfer/) A basic account management app, allowing transfer of DOTs between accounts.
@@ -57,6 +58,14 @@ There are additional environment UI flags that change both the theme and mode -
 - Running with `UI_MODE=light|full` switches from a full (the default) to a light mode interface that only has specific applications highlighted.
 - Running with `UI_THEME=substrate|polkadot` switches from a Polkadot theme (the default) to a Substrate-branded UI
 
+## I want to code around
+
+There is a base template availble [app-123code](packages/app-123code/) that acts as a simple starting point for adding additional apps to the UI. Alternatively if you just want some place where you can write some code, it does the trick.
+
+While it is hidden from the sidebar, it is accessible via [http://127.0.0.1:3000/#/123code](http://127.0.0.1:3000/#/123code)
+
+Be sure to follow the [app-123code/README.md](packages/app-template/README.md) instructions.
+
 ## Docker
 
 You can run a docker container via -

+ 1 - 1
jest.config.js

@@ -2,7 +2,7 @@ const config = require('@polkadot/dev-react/config/jest');
 
 module.exports = Object.assign({}, config, {
   moduleNameMapper: {
-    '@polkadot/app-(accounts|addresses|democracy|explorer|extrinsics|rpc|settings|staking|storage|toolbox|transfer)(.*)$': '<rootDir>/packages/app-$1/src/$2',
+    '@polkadot/app-(123code|accounts|addresses|democracy|explorer|extrinsics|rpc|settings|staking|status|storage|toolbox|transfer)(.*)$': '<rootDir>/packages/app-$1/src/$2',
     '@polkadot/ui-(api|app|params|reactive|signer)(.*)$': '<rootDir>/packages/ui-$1/src/$2',
     '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object',
     '\\.(css|less)$': 'empty/object'

+ 201 - 0
packages/app-123code/LICENSE

@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                    http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+  "License" shall mean the terms and conditions for use, reproduction,
+  and distribution as defined by Sections 1 through 9 of this document.
+
+  "Licensor" shall mean the copyright owner or entity authorized by
+  the copyright owner that is granting the License.
+
+  "Legal Entity" shall mean the union of the acting entity and all
+  other entities that control, are controlled by, or are under common
+  control with that entity. For the purposes of this definition,
+  "control" means (i) the power, direct or indirect, to cause the
+  direction or management of such entity, whether by contract or
+  otherwise, or (ii) ownership of fifty percent (50%) or more of the
+  outstanding shares, or (iii) beneficial ownership of such entity.
+
+  "You" (or "Your") shall mean an individual or Legal Entity
+  exercising permissions granted by this License.
+
+  "Source" form shall mean the preferred form for making modifications,
+  including but not limited to software source code, documentation
+  source, and configuration files.
+
+  "Object" form shall mean any form resulting from mechanical
+  transformation or translation of a Source form, including but
+  not limited to compiled object code, generated documentation,
+  and conversions to other media types.
+
+  "Work" shall mean the work of authorship, whether in Source or
+  Object form, made available under the License, as indicated by a
+  copyright notice that is included in or attached to the work
+  (an example is provided in the Appendix below).
+
+  "Derivative Works" shall mean any work, whether in Source or Object
+  form, that is based on (or derived from) the Work and for which the
+  editorial revisions, annotations, elaborations, or other modifications
+  represent, as a whole, an original work of authorship. For the purposes
+  of this License, Derivative Works shall not include works that remain
+  separable from, or merely link (or bind by name) to the interfaces of,
+  the Work and Derivative Works thereof.
+
+  "Contribution" shall mean any work of authorship, including
+  the original version of the Work and any modifications or additions
+  to that Work or Derivative Works thereof, that is intentionally
+  submitted to Licensor for inclusion in the Work by the copyright owner
+  or by an individual or Legal Entity authorized to submit on behalf of
+  the copyright owner. For the purposes of this definition, "submitted"
+  means any form of electronic, verbal, or written communication sent
+  to the Licensor or its representatives, including but not limited to
+  communication on electronic mailing lists, source code control systems,
+  and issue tracking systems that are managed by, or on behalf of, the
+  Licensor for the purpose of discussing and improving the Work, but
+  excluding communication that is conspicuously marked or otherwise
+  designated in writing by the copyright owner as "Not a Contribution."
+
+  "Contributor" shall mean Licensor and any individual or Legal Entity
+  on behalf of whom a Contribution has been received by Licensor and
+  subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  copyright license to reproduce, prepare Derivative Works of,
+  publicly display, publicly perform, sublicense, and distribute the
+  Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  (except as stated in this section) patent license to make, have made,
+  use, offer to sell, sell, import, and otherwise transfer the Work,
+  where such license applies only to those patent claims licensable
+  by such Contributor that are necessarily infringed by their
+  Contribution(s) alone or by combination of their Contribution(s)
+  with the Work to which such Contribution(s) was submitted. If You
+  institute patent litigation against any entity (including a
+  cross-claim or counterclaim in a lawsuit) alleging that the Work
+  or a Contribution incorporated within the Work constitutes direct
+  or contributory patent infringement, then any patent licenses
+  granted to You under this License for that Work shall terminate
+  as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+  Work or Derivative Works thereof in any medium, with or without
+  modifications, and in Source or Object form, provided that You
+  meet the following conditions:
+
+  (a) You must give any other recipients of the Work or
+      Derivative Works a copy of this License; and
+
+  (b) You must cause any modified files to carry prominent notices
+      stating that You changed the files; and
+
+  (c) You must retain, in the Source form of any Derivative Works
+      that You distribute, all copyright, patent, trademark, and
+      attribution notices from the Source form of the Work,
+      excluding those notices that do not pertain to any part of
+      the Derivative Works; and
+
+  (d) If the Work includes a "NOTICE" text file as part of its
+      distribution, then any Derivative Works that You distribute must
+      include a readable copy of the attribution notices contained
+      within such NOTICE file, excluding those notices that do not
+      pertain to any part of the Derivative Works, in at least one
+      of the following places: within a NOTICE text file distributed
+      as part of the Derivative Works; within the Source form or
+      documentation, if provided along with the Derivative Works; or,
+      within a display generated by the Derivative Works, if and
+      wherever such third-party notices normally appear. The contents
+      of the NOTICE file are for informational purposes only and
+      do not modify the License. You may add Your own attribution
+      notices within Derivative Works that You distribute, alongside
+      or as an addendum to the NOTICE text from the Work, provided
+      that such additional attribution notices cannot be construed
+      as modifying the License.
+
+  You may add Your own copyright statement to Your modifications and
+  may provide additional or different license terms and conditions
+  for use, reproduction, or distribution of Your modifications, or
+  for any such Derivative Works as a whole, provided Your use,
+  reproduction, and distribution of the Work otherwise complies with
+  the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+  any Contribution intentionally submitted for inclusion in the Work
+  by You to the Licensor shall be under the terms and conditions of
+  this License, without any additional terms or conditions.
+  Notwithstanding the above, nothing herein shall supersede or modify
+  the terms of any separate license agreement you may have executed
+  with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+  names, trademarks, service marks, or product names of the Licensor,
+  except as required for reasonable and customary use in describing the
+  origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+  agreed to in writing, Licensor provides the Work (and each
+  Contributor provides its Contributions) on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+  implied, including, without limitation, any warranties or conditions
+  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+  PARTICULAR PURPOSE. You are solely responsible for determining the
+  appropriateness of using or redistributing the Work and assume any
+  risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+  whether in tort (including negligence), contract, or otherwise,
+  unless required by applicable law (such as deliberate and grossly
+  negligent acts) or agreed to in writing, shall any Contributor be
+  liable to You for damages, including any direct, indirect, special,
+  incidental, or consequential damages of any character arising as a
+  result of this License or out of the use or inability to use the
+  Work (including but not limited to damages for loss of goodwill,
+  work stoppage, computer failure or malfunction, or any and all
+  other commercial damages or losses), even if such Contributor
+  has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+  the Work or Derivative Works thereof, You may choose to offer,
+  and charge a fee for, acceptance of support, warranty, indemnity,
+  or other liability obligations and/or rights consistent with this
+  License. However, in accepting such obligations, You may act only
+  on Your own behalf and on Your sole responsibility, not on behalf
+  of any other Contributor, and only if You agree to indemnify,
+  defend, and hold each Contributor harmless for any liability
+  incurred by, or claims asserted against, such Contributor by reason
+  of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+  To apply the Apache License to your work, attach the following
+  boilerplate notice, with the fields enclosed by brackets "[]"
+  replaced with your own identifying information. (Don't include
+  the brackets!)  The text should be enclosed in the appropriate
+  comment syntax for the file format. We also recommend that a
+  file or class name and description of purpose be included on the
+  same "printed page" as the copyright notice for easier
+  identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 24 - 0
packages/app-123code/README.md

@@ -0,0 +1,24 @@
+# @polkadot/app-123code
+
+A simple template to get started with adding an "app" to this UI. It contains the bare minimum for a nicely hackable app (if you just want to code _somewhere_) and the steps needed to create, add and register an new app that appears in the UI.
+
+## adding an app
+
+If you want to add a new app to the UI, this is the place to start.
+
+1. Duplicate this `app-123code` folder and give it an appropriate name, in this case we will select `app-example` to keep things clear.
+2. Edit the `apps-example/package.json` app description, i.e. the name, author and relevant overview.
+
+And we have the basic app source setup, time to get the tooling correct.
+
+3. Add the new app to the TypeScript config in root, `tsconfig.json`, i.e. an entry such as `"@polkadot/app-example/*": [ "packages/app-example/src/*" ],`
+4. Add the new app to the Jest config in root, `jest.config.js`, by just adding `|example` appropriately on the first line.
+5. In `apps/webpack.config.js` add `app-example` to the `const packages = [...]` list
+
+At this point the app should be buildable, but not quite reachable. The final step is to add it to the actual sidebar in `apps`.
+
+6. In `apps/src/routing/` duplicate the `template.ts` file to `example.ts` and edit it with the approprita information, including the hash link, name and icon (any icon name from semantic-ui-react/font-awesome 4 should be appropriate).
+7. In the above description file, the `isHidden` field needs to be toggled to make it appear - the base template is hidden by default.
+8. Finally add the `template` to the `apps/src/routing/index.ts` file at the appropriate place for both full and light mode (either optional)
+
+Yes. After all that we have things hooked up. Run `yarn start` and your new app (non-coded) should whow up. Now start having fun and building something great.

+ 16 - 0
packages/app-123code/package.json

@@ -0,0 +1,16 @@
+{
+  "name": "@polkadot/app-123code",
+  "version": "0.22.26",
+  "description": "A baasic app that shows the ropes on customisation",
+  "main": "index.js",
+  "scripts": {},
+  "author": "Jaco Greeff <jacogr@gmail.com>",
+  "maintainers": [
+    "Jaco Greeff <jacogr@gmail.com>"
+  ],
+  "license": "Apache-2.0",
+  "dependencies": {
+    "@babel/runtime": "^7.2.0",
+    "@polkadot/ui-app": "^0.22.26"
+  }
+}

+ 3 - 0
packages/app-123code/src/index.css

@@ -0,0 +1,3 @@
+/* Copyright 2017-2019 @polkadot/app-123code authors & contributors
+/* This software may be modified and distributed under the terms
+/* of the Apache-2.0 license. See the LICENSE file for details. */

+ 25 - 0
packages/app-123code/src/index.tsx

@@ -0,0 +1,25 @@
+// Copyright 2017-2019 @polkadot/app-123code authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
+
+import React from 'react';
+
+import './index.css';
+
+import translate from './translate';
+
+type Props = AppProps & I18nProps;
+
+type State = {};
+
+class App extends React.PureComponent<Props, State> {
+  render () {
+    return (
+      <main className='template--App'>empty</main>
+    );
+  }
+}
+
+export default translate(App);

+ 7 - 0
packages/app-123code/src/translate.ts

@@ -0,0 +1,7 @@
+// Copyright 2017-2019 @polkadot/app-123code authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { withNamespaces } from 'react-i18next';
+
+export default withNamespaces(['template', 'ui']);

+ 4 - 6
packages/app-accounts/src/index.tsx

@@ -2,10 +2,10 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { I18nProps } from '@polkadot/ui-app/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 import { TabItem } from '@polkadot/ui-app/Tabs';
 import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
-import { Actions, ActionStatus } from '@polkadot/ui-app/Status/types';
+import { Actions } from '@polkadot/ui-app/Status/types';
 
 import './index.css';
 
@@ -20,10 +20,8 @@ import Restore from './Restore';
 import Vanity from './Vanity';
 import translate from './translate';
 
-type Props = I18nProps & {
-  allAccounts?: SubjectInfo,
-  onStatusChange: (status: ActionStatus) => void,
-  basePath: string
+type Props = AppProps & I18nProps & {
+  allAccounts?: SubjectInfo
 };
 
 type State = {

+ 3 - 6
packages/app-addresses/src/index.tsx

@@ -2,9 +2,8 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { I18nProps } from '@polkadot/ui-app/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
 
 import './index.css';
 
@@ -18,10 +17,8 @@ import Creator from './Creator';
 import Editor from './Editor';
 import translate from './translate';
 
-type Props = I18nProps & {
-  allAddresses?: SubjectInfo,
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
+type Props = AppProps & I18nProps & {
+  allAddresses?: SubjectInfo
 };
 
 type Actions = 'create' | 'edit';

+ 2 - 6
packages/app-democracy/src/index.tsx

@@ -2,8 +2,7 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { BareProps } from '@polkadot/ui-app/types';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
+import { AppProps, BareProps } from '@polkadot/ui-app/types';
 
 import './index.css';
 
@@ -13,10 +12,7 @@ import Proposals from './Proposals';
 import Referendums from './Referendums';
 import Summary from './Summary';
 
-type Props = BareProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+type Props = AppProps & BareProps;
 
 export default class App extends React.PureComponent<Props> {
   render () {

+ 4 - 6
packages/app-explorer/src/BlockByHash/BlockByHash.tsx

@@ -62,9 +62,7 @@ class BlockByHash extends React.PureComponent<Props> {
 
     return (
       <section key='extrinsics'>
-        <h1>{t('block.extrinsics', {
-          defaultValue: 'extrinsics'
-        })}</h1>
+        <h1>{t('extrinsics')}</h1>
         <div className='explorer--BlockByHash-flexable'>
           {extrinsics.map(this.renderExtrinsic)}
         </div>
@@ -112,9 +110,9 @@ class BlockByHash extends React.PureComponent<Props> {
         <div>
           <AddressMini value={extrinsic.signature.signer} />
         </div>
-        <div className='explorer--BlockByHash-accountIndex'>{t('block.nonce', {
-          defaultValue: 'index'
-        })} {numberFormat(extrinsic.signature.nonce)}</div>
+        <div className='explorer--BlockByHash-accountIndex'>
+          {t('index')} {numberFormat(extrinsic.signature.nonce)}
+        </div>
       </div>
     );
   }

+ 2 - 6
packages/app-explorer/src/index.tsx

@@ -2,8 +2,7 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { BareProps } from '@polkadot/ui-app/types';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
+import { AppProps, BareProps } from '@polkadot/ui-app/types';
 
 import './index.css';
 
@@ -13,10 +12,7 @@ import { Route, Switch } from 'react-router';
 import BlockByHash from './BlockByHash';
 import Main from './Main';
 
-type Props = BareProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+type Props = AppProps & BareProps;
 
 export default class ExplorerApp extends React.Component<Props> {
   render () {

+ 4 - 6
packages/app-extrinsics/src/index.tsx

@@ -2,8 +2,8 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { ActionStatus, QueueProps } from '@polkadot/ui-app/Status/types';
-import { I18nProps } from '@polkadot/ui-app/types';
+import { QueueProps } from '@polkadot/ui-app/Status/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 
 import './index.css';
 
@@ -16,10 +16,8 @@ import translate from './translate';
 
 import Selection from './Selection';
 
-type Props = I18nProps & {
-  accountAll?: Array<any>,
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
+type Props = AppProps & I18nProps & {
+  accountAll?: Array<any>
 };
 
 class ExtrinsicsApp extends React.PureComponent<Props> {

+ 201 - 0
packages/app-nodeinfo/LICENSE

@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                    http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+  "License" shall mean the terms and conditions for use, reproduction,
+  and distribution as defined by Sections 1 through 9 of this document.
+
+  "Licensor" shall mean the copyright owner or entity authorized by
+  the copyright owner that is granting the License.
+
+  "Legal Entity" shall mean the union of the acting entity and all
+  other entities that control, are controlled by, or are under common
+  control with that entity. For the purposes of this definition,
+  "control" means (i) the power, direct or indirect, to cause the
+  direction or management of such entity, whether by contract or
+  otherwise, or (ii) ownership of fifty percent (50%) or more of the
+  outstanding shares, or (iii) beneficial ownership of such entity.
+
+  "You" (or "Your") shall mean an individual or Legal Entity
+  exercising permissions granted by this License.
+
+  "Source" form shall mean the preferred form for making modifications,
+  including but not limited to software source code, documentation
+  source, and configuration files.
+
+  "Object" form shall mean any form resulting from mechanical
+  transformation or translation of a Source form, including but
+  not limited to compiled object code, generated documentation,
+  and conversions to other media types.
+
+  "Work" shall mean the work of authorship, whether in Source or
+  Object form, made available under the License, as indicated by a
+  copyright notice that is included in or attached to the work
+  (an example is provided in the Appendix below).
+
+  "Derivative Works" shall mean any work, whether in Source or Object
+  form, that is based on (or derived from) the Work and for which the
+  editorial revisions, annotations, elaborations, or other modifications
+  represent, as a whole, an original work of authorship. For the purposes
+  of this License, Derivative Works shall not include works that remain
+  separable from, or merely link (or bind by name) to the interfaces of,
+  the Work and Derivative Works thereof.
+
+  "Contribution" shall mean any work of authorship, including
+  the original version of the Work and any modifications or additions
+  to that Work or Derivative Works thereof, that is intentionally
+  submitted to Licensor for inclusion in the Work by the copyright owner
+  or by an individual or Legal Entity authorized to submit on behalf of
+  the copyright owner. For the purposes of this definition, "submitted"
+  means any form of electronic, verbal, or written communication sent
+  to the Licensor or its representatives, including but not limited to
+  communication on electronic mailing lists, source code control systems,
+  and issue tracking systems that are managed by, or on behalf of, the
+  Licensor for the purpose of discussing and improving the Work, but
+  excluding communication that is conspicuously marked or otherwise
+  designated in writing by the copyright owner as "Not a Contribution."
+
+  "Contributor" shall mean Licensor and any individual or Legal Entity
+  on behalf of whom a Contribution has been received by Licensor and
+  subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  copyright license to reproduce, prepare Derivative Works of,
+  publicly display, publicly perform, sublicense, and distribute the
+  Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  (except as stated in this section) patent license to make, have made,
+  use, offer to sell, sell, import, and otherwise transfer the Work,
+  where such license applies only to those patent claims licensable
+  by such Contributor that are necessarily infringed by their
+  Contribution(s) alone or by combination of their Contribution(s)
+  with the Work to which such Contribution(s) was submitted. If You
+  institute patent litigation against any entity (including a
+  cross-claim or counterclaim in a lawsuit) alleging that the Work
+  or a Contribution incorporated within the Work constitutes direct
+  or contributory patent infringement, then any patent licenses
+  granted to You under this License for that Work shall terminate
+  as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+  Work or Derivative Works thereof in any medium, with or without
+  modifications, and in Source or Object form, provided that You
+  meet the following conditions:
+
+  (a) You must give any other recipients of the Work or
+      Derivative Works a copy of this License; and
+
+  (b) You must cause any modified files to carry prominent notices
+      stating that You changed the files; and
+
+  (c) You must retain, in the Source form of any Derivative Works
+      that You distribute, all copyright, patent, trademark, and
+      attribution notices from the Source form of the Work,
+      excluding those notices that do not pertain to any part of
+      the Derivative Works; and
+
+  (d) If the Work includes a "NOTICE" text file as part of its
+      distribution, then any Derivative Works that You distribute must
+      include a readable copy of the attribution notices contained
+      within such NOTICE file, excluding those notices that do not
+      pertain to any part of the Derivative Works, in at least one
+      of the following places: within a NOTICE text file distributed
+      as part of the Derivative Works; within the Source form or
+      documentation, if provided along with the Derivative Works; or,
+      within a display generated by the Derivative Works, if and
+      wherever such third-party notices normally appear. The contents
+      of the NOTICE file are for informational purposes only and
+      do not modify the License. You may add Your own attribution
+      notices within Derivative Works that You distribute, alongside
+      or as an addendum to the NOTICE text from the Work, provided
+      that such additional attribution notices cannot be construed
+      as modifying the License.
+
+  You may add Your own copyright statement to Your modifications and
+  may provide additional or different license terms and conditions
+  for use, reproduction, or distribution of Your modifications, or
+  for any such Derivative Works as a whole, provided Your use,
+  reproduction, and distribution of the Work otherwise complies with
+  the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+  any Contribution intentionally submitted for inclusion in the Work
+  by You to the Licensor shall be under the terms and conditions of
+  this License, without any additional terms or conditions.
+  Notwithstanding the above, nothing herein shall supersede or modify
+  the terms of any separate license agreement you may have executed
+  with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+  names, trademarks, service marks, or product names of the Licensor,
+  except as required for reasonable and customary use in describing the
+  origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+  agreed to in writing, Licensor provides the Work (and each
+  Contributor provides its Contributions) on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+  implied, including, without limitation, any warranties or conditions
+  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+  PARTICULAR PURPOSE. You are solely responsible for determining the
+  appropriateness of using or redistributing the Work and assume any
+  risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+  whether in tort (including negligence), contract, or otherwise,
+  unless required by applicable law (such as deliberate and grossly
+  negligent acts) or agreed to in writing, shall any Contributor be
+  liable to You for damages, including any direct, indirect, special,
+  incidental, or consequential damages of any character arising as a
+  result of this License or out of the use or inability to use the
+  Work (including but not limited to damages for loss of goodwill,
+  work stoppage, computer failure or malfunction, or any and all
+  other commercial damages or losses), even if such Contributor
+  has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+  the Work or Derivative Works thereof, You may choose to offer,
+  and charge a fee for, acceptance of support, warranty, indemnity,
+  or other liability obligations and/or rights consistent with this
+  License. However, in accepting such obligations, You may act only
+  on Your own behalf and on Your sole responsibility, not on behalf
+  of any other Contributor, and only if You agree to indemnify,
+  defend, and hold each Contributor harmless for any liability
+  incurred by, or claims asserted against, such Contributor by reason
+  of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+  To apply the Apache License to your work, attach the following
+  boilerplate notice, with the fields enclosed by brackets "[]"
+  replaced with your own identifying information. (Don't include
+  the brackets!)  The text should be enclosed in the appropriate
+  comment syntax for the file format. We also recommend that a
+  file or class name and description of purpose be included on the
+  same "printed page" as the copyright notice for easier
+  identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 1 - 0
packages/app-nodeinfo/README.md

@@ -0,0 +1 @@
+# @polkadot/app-nodeinfo

+ 16 - 0
packages/app-nodeinfo/package.json

@@ -0,0 +1,16 @@
+{
+  "name": "@polkadot/app-nodeinfo",
+  "version": "0.22.26",
+  "description": "Node status and information",
+  "main": "index.js",
+  "scripts": {},
+  "author": "Jaco Greeff <jacogr@gmail.com>",
+  "maintainers": [
+    "Jaco Greeff <jacogr@gmail.com>"
+  ],
+  "license": "Apache-2.0",
+  "dependencies": {
+    "@babel/runtime": "^7.2.0",
+    "@polkadot/ui-app": "^0.22.26"
+  }
+}

+ 69 - 0
packages/app-nodeinfo/src/Peers.tsx

@@ -0,0 +1,69 @@
+// Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { I18nProps } from '@polkadot/ui-app/types';
+
+import React from 'react';
+import { PeerInfo } from '@polkadot/types';
+import { numberFormat } from '@polkadot/ui-reactive/util/index';
+
+import translate from './translate';
+
+type Props = I18nProps & {
+  peers?: Array<PeerInfo> | null
+};
+
+class Peers extends React.PureComponent<Props> {
+  render () {
+    const { t } = this.props;
+
+    return (
+      <section className='status--Peers'>
+        <h1>{t('connected peers')}</h1>
+        {this.renderPeers()}
+      </section>
+    );
+  }
+
+  private renderPeers () {
+    const { peers, t } = this.props;
+
+    if (!peers || !peers.length) {
+      return t('no peers connected');
+    }
+
+    return (
+      <article>
+        <table>
+          <thead>
+            <tr>
+              <th className='number'>{t('index')}</th>
+              <th className='roles'>{t('role')}</th>
+              <th className='peerid'>{t('peer id')}</th>
+              <th className='number'>{t('best #')}</th>
+              <th className='hash'>{t('best hash')}</th>
+            </tr>
+          </thead>
+          <tbody>
+            {peers.map(this.renderPeer)}
+          </tbody>
+        </table>
+      </article>
+    );
+  }
+
+  private renderPeer = (peer: PeerInfo) => {
+    return (
+      <tr key={`peer:${peer.index.toNumber()}`}>
+        <td className='number'>{peer.index.toNumber()}</td>
+        <td className='roles'>{peer.roles.toString().toLowerCase()}</td>
+        <td className='peerid'>{peer.peerId.toString()}</td>
+        <td className='number'>{numberFormat(peer.bestNumber)}</td>
+        <td className='hash'>{peer.bestHash.toHex()}</td>
+      </tr>
+    );
+  }
+}
+
+export default translate(Peers);

+ 93 - 0
packages/app-nodeinfo/src/Pending.tsx

@@ -0,0 +1,93 @@
+// Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { I18nProps } from '@polkadot/ui-app/types';
+
+import React from 'react';
+import { AddressMini, Call } from '@polkadot/ui-app/index';
+import { Extrinsic, Method } from '@polkadot/types';
+import { numberFormat } from '@polkadot/ui-reactive/util/index';
+
+import translate from './translate';
+
+type Props = I18nProps & {
+  extrinsics?: Array<Extrinsic> | null
+};
+
+// FIXME - this is basically a copy-and paste of what happens in app-explorer BlockByHash
+// make rendering extrinsics a proper component
+class Pending extends React.PureComponent<Props> {
+  render () {
+    const { t } = this.props;
+
+    return (
+      <section>
+        <h1>{t('pending extrinsics')}</h1>
+        {this.renderExtrinsics()}
+      </section>
+    );
+  }
+
+  private renderExtrinsics () {
+    const { extrinsics, t } = this.props;
+
+    if (!extrinsics || !extrinsics.length) {
+      return t('no pending extrinsics are in the queue');
+    }
+
+    return (
+      <div className='explorer--BlockByHash-flexable'>
+        {extrinsics.map(this.renderExtrinsic)}
+      </div>
+    );
+  }
+
+  // FIXME This is _very_ similar to what we have in exporer
+  private renderExtrinsic = (extrinsic: Extrinsic, index: number) => {
+    const { meta, method, section } = Method.findFunction(extrinsic.callIndex);
+
+    return (
+      <div
+        className='explorer--BlockByHash-block'
+        key={`extrinsic:${index}`}
+      >
+        <article className='explorer--Container'>
+          <div className='header'>
+            <h3>
+              #{numberFormat(index)}:&nbsp;{section}.{method}
+            </h3>
+            <div className='description'>{
+              meta && meta.documentation && meta.documentation.length
+                ? meta.documentation.map((doc) => doc.toString()).join(' ')
+                : ''
+            }</div>
+            {this.renderSigner(extrinsic)}
+          </div>
+          <Call value={extrinsic} />
+        </article>
+      </div>
+    );
+  }
+
+  private renderSigner (extrinsic: Extrinsic) {
+    const { t } = this.props;
+
+    if (!extrinsic.signature.isSigned) {
+      return null;
+    }
+
+    return (
+      <div className='explorer--BlockByHash-header-right'>
+        <div>
+          <AddressMini value={extrinsic.signature.signer} />
+        </div>
+        <div className='explorer--BlockByHash-accountIndex'>
+          {t('index')} {numberFormat(extrinsic.signature.nonce)}
+        </div>
+      </div>
+    );
+  }
+}
+
+export default translate(Pending);

+ 63 - 0
packages/app-nodeinfo/src/Summary.tsx

@@ -0,0 +1,63 @@
+// Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { I18nProps } from '@polkadot/ui-app/types';
+import { Info } from './types';
+
+import React from 'react';
+import { CardSummary } from '@polkadot/ui-app/index';
+import { BestNumber } from '@polkadot/ui-reactive/index';
+
+import translate from './translate';
+
+type Props = I18nProps & {
+  info: Info;
+};
+
+class Summary extends React.PureComponent<Props> {
+  render () {
+    const { info = {}, t } = this.props;
+
+    return (
+      <summary>
+        <section>
+          <CardSummary label={t('total peers')}>
+            {
+              info.health
+                ? `${info.health.peers.toNumber()}`
+                : '-'
+            }
+          </CardSummary>
+          <CardSummary label={t('syncing')}>
+            {
+              info.health
+                ? (
+                  info.health.isSyncing.valueOf()
+                    ? t('yes')
+                    : t('no')
+                )
+                : '-'
+            }
+          </CardSummary>
+        </section>
+        <section>
+          <CardSummary label={t('queued tx')}>
+            {
+              info.extrinsics
+                ? `${info.extrinsics.length}`
+                : '-'
+            }
+          </CardSummary>
+        </section>
+        <section>
+          <CardSummary label={t('best')}>
+            <BestNumber />
+          </CardSummary>
+        </section>
+      </summary>
+    );
+  }
+}
+
+export default translate(Summary);

+ 45 - 0
packages/app-nodeinfo/src/index.css

@@ -0,0 +1,45 @@
+/* Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+/* This software may be modified and distributed under the terms
+/* of the Apache-2.0 license. See the LICENSE file for details. */
+
+.status--Peers {
+  table {
+    width: 100%;
+
+    td, th {
+      padding: 0.25rem 0.5rem;
+      text-align: left;
+      white-space: nowrap;
+
+      &.hash {
+        font-family: monospace;
+        max-width: 0;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        width: 100%;
+      }
+
+      &.number {
+        text-align: right;
+      }
+
+      &.peerid {
+        text-align: left;
+      }
+
+      &.roles {
+        text-align: center;
+      }
+    }
+  }
+
+  tbody {
+    tr {
+      width: 100%;
+
+      &:nth-child(odd) {
+        background-color: #f2f2f2;
+      }
+    }
+  }
+}

+ 88 - 0
packages/app-nodeinfo/src/index.tsx

@@ -0,0 +1,88 @@
+// Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { ApiProps } from '@polkadot/ui-api/types';
+import { AppProps } from '@polkadot/ui-app/types';
+import { Info } from './types';
+
+import React from 'react';
+import { withApi } from '@polkadot/ui-api/index';
+import { Health, PeerInfo, PendingExtrinsics } from '@polkadot/types';
+
+import './index.css';
+
+import Peers from './Peers';
+import Pending from './Pending';
+import Summary from './Summary';
+
+const POLL_TIMEOUT = 10000;
+
+type Props = ApiProps & AppProps;
+
+type State = {
+  info?: Info,
+  timerId?: number;
+};
+
+class App extends React.PureComponent<Props, State> {
+  private isActive: boolean = true;
+  state: State = {};
+
+  componentDidMount () {
+    this.getStatus().catch(() => {
+      // ignore
+    });
+  }
+
+  componentWillUnmount () {
+    const { timerId } = this.state;
+
+    this.isActive = false;
+
+    if (timerId) {
+      window.clearTimeout(timerId);
+    }
+  }
+
+  private setInfo (info?: Info) {
+    if (!this.isActive) {
+      return;
+    }
+
+    this.setState({
+      info,
+      timerId: window.setTimeout(this.getStatus, POLL_TIMEOUT)
+    });
+  }
+
+  private getStatus = async () => {
+    const { api } = this.props;
+
+    try {
+      const [health, peers, extrinsics] = await Promise.all([
+        api.rpc.system.health() as Promise<Health>,
+        api.rpc.system.peers() as any as Promise<Array<PeerInfo>>,
+        api.rpc.author.pendingExtrinsics() as Promise<PendingExtrinsics>
+      ]);
+
+      this.setInfo({ extrinsics, health, peers });
+    } catch (error) {
+      this.setInfo();
+    }
+  }
+
+  render () {
+    const { info = {} } = this.state;
+
+    return (
+      <main className='status--App'>
+        <Summary info={info} />
+        <Peers peers={info.peers} />
+        <Pending extrinsics={info.extrinsics} />
+      </main>
+    );
+  }
+}
+
+export default withApi(App);

+ 7 - 0
packages/app-nodeinfo/src/translate.ts

@@ -0,0 +1,7 @@
+// Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { withNamespaces } from 'react-i18next';
+
+export default withNamespaces(['status', 'ui']);

+ 11 - 0
packages/app-nodeinfo/src/types.ts

@@ -0,0 +1,11 @@
+// Copyright 2017-2019 @polkadot/app-nodeinfo authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { Health, PeerInfo, PendingExtrinsics } from '@polkadot/types';
+
+export type Info = {
+  health?: Health | null,
+  peers?: Array<PeerInfo> | null,
+  extrinsics?: PendingExtrinsics | null
+};

+ 3 - 6
packages/app-rpc/src/index.tsx

@@ -2,8 +2,8 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { BareProps } from '@polkadot/ui-app/types';
-import { ActionStatus, QueueProps } from '@polkadot/ui-app/Status/types';
+import { AppProps, BareProps } from '@polkadot/ui-app/types';
+import { QueueProps } from '@polkadot/ui-app/Status/types';
 
 import './index.css';
 
@@ -13,10 +13,7 @@ import { QueueConsumer } from '@polkadot/ui-app/Status/Context';
 import Results from './Results';
 import Selection from './Selection';
 
-type Props = BareProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+type Props = AppProps & BareProps;
 
 export default class RpcApp extends React.PureComponent<Props> {
   render () {

+ 0 - 5
packages/app-settings/src/index.css

@@ -1,8 +1,3 @@
 /* Copyright 2017-2019 @polkadot/app-settings authors & contributors
 /* This software may be modified and distributed under the terms
 /* of the Apache-2.0 license. See the LICENSE file for details. */
-
-.settings--Transfer-info {
-  display: flex;
-  flex-direction: row;
-}

+ 2 - 6
packages/app-settings/src/index.tsx

@@ -2,14 +2,13 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { I18nProps } from '@polkadot/ui-app/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 import { SettingsStruct } from '@polkadot/ui-settings/types';
 
 import React from 'react';
 import store from 'store';
 import typeRegistry from '@polkadot/types/codec/typeRegistry';
 import { Button, Dropdown, InputFile } from '@polkadot/ui-app/index';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
 import settings from '@polkadot/ui-settings';
 import { u8aToString } from '@polkadot/util';
 
@@ -17,10 +16,7 @@ import './index.css';
 
 import translate from './translate';
 
-type Props = I18nProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+type Props = AppProps & I18nProps;
 
 type State = SettingsStruct & {
   types?: { [index: string]: any } | null,

+ 2 - 5
packages/app-staking/src/index.tsx

@@ -3,8 +3,7 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { DerivedBalancesMap } from '@polkadot/api-derive/types';
-import { I18nProps } from '@polkadot/ui-app/types';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 
 import React from 'react';
 import { AccountId, Balance } from '@polkadot/types';
@@ -19,11 +18,9 @@ import translate from './translate';
 
 type Actions = 'actions' | 'overview';
 
-type Props = I18nProps & {
-  basePath: string,
+type Props = AppProps & I18nProps & {
   balances?: DerivedBalancesMap,
   intentions?: Array<AccountId>,
-  onStatusChange: (status: ActionStatus) => void,
   session_validators?: Array<AccountId>
 };
 

+ 2 - 7
packages/app-storage/src/index.tsx

@@ -2,9 +2,7 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { I18nProps } from '@polkadot/ui-app/types';
-
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 import { QueryTypes } from './types';
 
 import './index.css';
@@ -15,10 +13,7 @@ import Queries from './Queries';
 import Selection from './Selection/index';
 import translate from './translate';
 
-type Props = I18nProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+type Props = AppProps & I18nProps;
 
 type State = {
   queue: Array<QueryTypes>

+ 2 - 6
packages/app-toolbox/src/index.tsx

@@ -2,8 +2,7 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { I18nProps } from '@polkadot/ui-app/types';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
+import { AppProps, I18nProps } from '@polkadot/ui-app/types';
 
 import './index.css';
 
@@ -17,10 +16,7 @@ import translate from './translate';
 
 type Actions = 'hash' | 'sign' | 'verify';
 
-type Props = I18nProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+type Props = AppProps & I18nProps;
 
 type State = {
   action: Actions

+ 69 - 44
packages/apps/src/SideBar/index.tsx

@@ -29,7 +29,7 @@ const LOGO = LOGOS.get(settings.uiTheme) || polkadotLogo;
 
 class SideBar extends React.PureComponent<Props> {
   render () {
-    const { children, t } = this.props;
+    const { children } = this.props;
 
     return (
       <div className='apps--SideBar'>
@@ -37,56 +37,81 @@ class SideBar extends React.PureComponent<Props> {
           secondary
           vertical
         >
-          <img
-            alt='polkadot'
-            className='apps--SideBar-logo'
-            src={LOGO}
-          />
-          {
-            routing.routes
-              .filter((route) =>
-                !route || !route.isHidden
-              )
-              .map((route, index) => (
-                route
-                  ? (
-                    <Item
-                      key={route.name}
-                      t={t}
-                      route={route}
-                    />
-                  )
-                  : (
-                    <Menu.Divider
-                      hidden
-                      key={index}
-                    />
-                  )
-              ))
-          }
+          {this.renderLogo()}
+          {this.renderRoutes()}
           <Menu.Divider hidden />
-          <Menu.Item className='apps--SideBar-Item'>
-            <a
-              className='apps--SideBar-Item-NavLink'
-              href='https://github.com/polkadot-js/apps'
-            >
-              <Icon name='github' /> GitHub
-            </a>
-          </Menu.Item>
-          <Menu.Item className='apps--SideBar-Item'>
-            <a
-              className='apps--SideBar-Item-NavLink'
-              href='https://github.com/w3f/Web3-wiki/wiki/Polkadot'
-            >
-              <Icon name='book' /> Wiki
-            </a>
-          </Menu.Item>
+          {this.renderGithub()}
+          {this.renderWiki()}
           <Menu.Divider hidden />
           {children}
         </Menu>
       </div>
     );
   }
+
+  private renderLogo () {
+    return (
+      <img
+        alt='polkadot'
+        className='apps--SideBar-logo'
+        src={LOGO}
+      />
+    );
+  }
+
+  private renderRoutes () {
+    const { t } = this.props;
+
+    return routing.routes
+      .filter((route) =>
+        !route || !route.isHidden
+      )
+      .map((route, index) => (
+        route
+          ? (
+            <Item
+              key={route.name}
+              t={t}
+              route={route}
+            />
+          )
+          : (
+            <Menu.Divider
+              hidden
+              key={index}
+            />
+          )
+      ));
+  }
+
+  private renderGithub () {
+    return (
+      <Menu.Item className='apps--SideBar-Item'>
+        <a
+          className='apps--SideBar-Item-NavLink'
+          href='https://github.com/polkadot-js/apps'
+        >
+          <Icon name='github' /> GitHub
+        </a>
+      </Menu.Item>
+    );
+  }
+
+  private renderWiki () {
+    return null;
+
+    // disabled for now, we need the space
+    // return (
+    //   <Menu.Item className='apps--SideBar-Item'>
+    //     <a
+    //       className='apps--SideBar-Item-NavLink'
+    //       href='https://github.com/w3f/Web3-wiki/wiki/Polkadot'
+    //     >
+    //       <Icon name='book' /> Wiki
+    //     </a>
+    //   </Menu.Item>
+    // );
+  }
 }
 
 export default translate(SideBar);

+ 20 - 0
packages/apps/src/routing/123code.ts

@@ -0,0 +1,20 @@
+// Copyright 2017-2019 @polkadot/apps authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { Routes } from '../types';
+
+import Template from '@polkadot/app-123code/index';
+
+export default ([
+  {
+    Component: Template,
+    i18n: {
+      defaultValue: 'Template'
+    },
+    icon: 'th',
+    isApiGated: true,
+    isHidden: true,
+    name: '123code'
+  }
+] as Routes);

+ 8 - 2
packages/apps/src/routing/index.ts

@@ -6,11 +6,13 @@ import { Routing, Routes } from '../types';
 
 import appSettings from '@polkadot/ui-settings';
 
+import template from './123code';
 import accounts from './accounts';
 import addresses from './addresses';
 import democracy from './democracy';
 import explorer from './explorer';
 import extrinsics from './extrinsics';
+import nodeinfo from './nodeinfo';
 import rpc from './rpc';
 import settings from './settings';
 import staking from './staking';
@@ -27,7 +29,9 @@ const routes: Routes = appSettings.uiMode === 'light'
     accounts,
     addresses,
     null,
-    settings
+    settings,
+    nodeinfo,
+    template
   )
   : ([] as Routes).concat(
     explorer,
@@ -42,8 +46,10 @@ const routes: Routes = appSettings.uiMode === 'light'
     extrinsics,
     null,
     settings,
+    nodeinfo,
     rpc,
-    toolbox
+    toolbox,
+    template
   );
 
 export default ({

+ 20 - 0
packages/apps/src/routing/nodeinfo.ts

@@ -0,0 +1,20 @@
+// Copyright 2017-2019 @polkadot/apps authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { Routes } from '../types';
+
+import Nodeinfo from '@polkadot/app-nodeinfo/index';
+
+export default ([
+  {
+    Component: Nodeinfo,
+    i18n: {
+      defaultValue: 'Node info'
+    },
+    icon: 'tty',
+    isApiGated: true,
+    isHidden: false,
+    name: 'nodeinfo'
+  }
+] as Routes);

+ 2 - 6
packages/apps/src/types.ts

@@ -3,13 +3,9 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { SemanticICONS } from 'semantic-ui-react/dist/commonjs';
-import { BareProps } from '@polkadot/ui-app/types';
-import { ActionStatus } from '@polkadot/ui-app/Status/types';
+import { AppProps, BareProps } from '@polkadot/ui-app/types';
 
-export type RouteProps = BareProps & {
-  basePath: string,
-  onStatusChange: (status: ActionStatus) => void
-};
+export type RouteProps = AppProps & BareProps;
 
 export type Route = {
   Component: React.ComponentType<RouteProps>,

+ 2 - 0
packages/apps/webpack.config.js

@@ -19,7 +19,9 @@ const packages = [
   'app-rpc',
   'app-settings',
   'app-staking',
+  'app-nodeinfo',
   'app-storage',
+  'app-123code',
   'app-toolbox',
   'app-transfer',
   'ui-api',

+ 6 - 0
packages/ui-app/src/types.ts

@@ -4,6 +4,7 @@
 
 import { WithNamespaces } from 'react-i18next';
 import { RpcRxInterface } from '@polkadot/rpc-rx/types';
+import { ActionStatus } from '@polkadot/ui-app/Status/types';
 
 export type BareProps = {
   className?: string,
@@ -12,6 +13,11 @@ export type BareProps = {
   }
 };
 
+export type AppProps = {
+  basePath: string,
+  onStatusChange: (status: ActionStatus) => void
+};
+
 export type I18nProps = BareProps & WithNamespaces;
 
 export type BaseContext = {

+ 2 - 0
tsconfig.json

@@ -3,6 +3,7 @@
   "compilerOptions": {
     "baseUrl": ".",
     "paths": {
+      "@polkadot/app-123code/*": [ "packages/app-123code/src/*" ],
       "@polkadot/app-accounts/*": [ "packages/app-accounts/src/*" ],
       "@polkadot/app-addresses/*": [ "packages/app-addresses/src/*" ],
       "@polkadot/app-democracy/*": [ "packages/app-democracy/src/*" ],
@@ -11,6 +12,7 @@
       "@polkadot/app-rpc/*": [ "packages/app-rpc/src/*" ],
       "@polkadot/app-settings/*": [ "packages/app-settings/src/*" ],
       "@polkadot/app-staking/*": [ "packages/app-staking/src/*" ],
+      "@polkadot/app-nodeinfo/*": [ "packages/app-nodeinfo/src/*" ],
       "@polkadot/app-storage/*": [ "packages/app-storage/src/*" ],
       "@polkadot/app-toolbox/*": [ "packages/app-toolbox/src/*" ],
       "@polkadot/app-transfer/*": [ "packages/app-transfer/src/*" ],